<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 4.2.1">
  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">


<link rel="stylesheet" href="/lib/font-awesome/css/all.min.css">

<script id="hexo-configurations">
    var NexT = window.NexT || {};
    var CONFIG = {"hostname":"yoursite.com","root":"/","scheme":"Gemini","version":"7.8.0","exturl":false,"sidebar":{"position":"left","display":"always","padding":18,"offset":12,"onmobile":false},"copycode":{"enable":false,"show_result":false,"style":null},"back2top":{"enable":true,"sidebar":false,"scrollpercent":false},"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"algolia":{"hits":{"per_page":10},"labels":{"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}},"localsearch":{"enable":false,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}}};
  </script>

  <meta name="description" content="郭鹏松的个人博客">
<meta property="og:type" content="website">
<meta property="og:title" content="Albert Guo">
<meta property="og:url" content="http://yoursite.com/page/2/index.html">
<meta property="og:site_name" content="Albert Guo">
<meta property="og:description" content="郭鹏松的个人博客">
<meta property="og:locale" content="zh_CN">
<meta property="article:author" content="Albert Guo">
<meta name="twitter:card" content="summary">

<link rel="canonical" href="http://yoursite.com/page/2/">


<script id="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome : true,
    isPost : false,
    lang   : 'zh-CN'
  };
</script>

  <title>Albert Guo</title>
  






  <noscript>
  <style>
  .use-motion .brand,
  .use-motion .menu-item,
  .sidebar-inner,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line-before i { left: initial; }
  .use-motion .logo-line-after i { right: initial; }
  </style>
</noscript>

</head>

<body itemscope itemtype="http://schema.org/WebPage">
  <div class="container use-motion">
    <div class="headband"></div>

    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏">
      <span class="toggle-line toggle-line-first"></span>
      <span class="toggle-line toggle-line-middle"></span>
      <span class="toggle-line toggle-line-last"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <span class="logo-line-before"><i></i></span>
      <h1 class="site-title">Albert Guo</h1>
      <span class="logo-line-after"><i></i></span>
    </a>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
    </div>
  </div>
</div>




<nav class="site-nav">
  <ul id="menu" class="main-menu menu">
        <li class="menu-item menu-item-home">

    <a href="/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a>

  </li>
        <li class="menu-item menu-item-categories">

    <a href="/categories/" rel="section"><i class="fa fa-bars fa-fw"></i>分类</a>

  </li>
        <li class="menu-item menu-item-about">

    <a href="/about/" rel="section"><i class="fa fa-address-card fa-fw"></i>简介</a>

  </li>
        <li class="menu-item menu-item-webpack">

    <a href="/webpack/" rel="section"><i class="fa fa-bookmark fa-fw"></i>webpack</a>

  </li>
        <li class="menu-item menu-item-javascript">

    <a href="/JavaScript/" rel="section"><i class="fa fa-bookmark fa-fw"></i>ECMAScript 5.1</a>

  </li>
        <li class="menu-item menu-item-ecmascript6">

    <a href="/ECMAScript6/" rel="section"><i class="fa fa-bookmark fa-fw"></i>ECMAScript 6</a>

  </li>
        <li class="menu-item menu-item-react">

    <a href="/React/" rel="section"><i class="fa fa-bookmark fa-fw"></i>React</a>

  </li>
  </ul>
</nav>




</div>
    </header>

    
  <div class="back-to-top">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>


    <main class="main">
      <div class="main-inner">
        <div class="content-wrap">
          

          <div class="content index posts-expand">
            
      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://yoursite.com/2018/08/29/git-%E5%9F%BA%E6%9C%AC%E4%BD%BF%E7%94%A8/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="https://inews.gtimg.com/newsapp_ls/0/12122532695/0">
      <meta itemprop="name" content="Albert Guo">
      <meta itemprop="description" content="郭鹏松的个人博客">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Albert Guo">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/2018/08/29/git-%E5%9F%BA%E6%9C%AC%E4%BD%BF%E7%94%A8/" class="post-title-link" itemprop="url">git 基本使用</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2018-08-29 19:22:13" itemprop="dateCreated datePublished" datetime="2018-08-29T19:22:13+08:00">2018-08-29</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2020-08-23 22:19:57" itemprop="dateModified" datetime="2020-08-23T22:19:57+08:00">2020-08-23</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/git/" itemprop="url" rel="index"><span itemprop="name">git</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <blockquote>
<p>声明，本教程摘录于廖雪峰Git教程，非本人原创。文中部分内容来自于个人工作积累编写，仅供学习参考。</p>
</blockquote>
<p>原创网址： <a href="https://www.liaoxuefeng.com/wiki/896043488029600/896067008724000" target="_blank" rel="noopener">Git教程</a></p>
<h1 id="历史"><a href="#历史" class="headerlink" title="历史"></a>历史</h1><p>很多人都知道， ==Linus== 在 1991 年创建了开源的 Linux，从此，Linux 系统不断发展，已经成为最大的服务器系统软件了。</p>
<p>Linus 虽然创建了 Linux，但 Linux 的壮大是靠全世界热心的志愿者参与的，这么多人在世界各地为 Linux 编写代码，那Linux的代码是如何管理的呢？</p>
<p>事实是，在 2002 年以前，世界各地的志愿者把源代码文件通过 diff 的方式发给 Linus，然后由 Linus 本人通过手工方式合并代码！</p>
<p>你也许会想，为什么 Linus 不把 Linux 代码放到版本控制系统里呢？不是有 CVS、SVN 这些免费的版本控制系统吗？因为 Linus 坚定地反对 CVS 和 SVN，这些集中式的版本控制系统不但速度慢，而且必须联网才能使用。有一些商用的版本控制系统，虽然比 CVS、SVN 好用，但那是付费的，和 Linux 的开源精神不符。</p>
<p>不过，到了 2002 年，Linux 系统已经发展了十年了，代码库之大让 Linus 很难继续通过手工方式管理了，社区的弟兄们也对这种方式表达了强烈不满，于是 Linus 选择了一个商业的版本控制系统 BitKeeper，BitKeeper 的东家 BitMover 公司出于人道主义精神，授权 Linux 社区免费使用这个版本控制系统。</p>
<p>安定团结的大好局面在 2005 年就被打破了，原因是 Linux 社区牛人聚集，不免沾染了一些梁山好汉的江湖习气。开发 Samba 的 Andrew 试图破解 BitKeeper 的协议（这么干的其实也不只他一个），被BitMover公司发现了（监控工作做得不错！），于是 BitMover 公司怒了，要收回 Linux 社区的免费使用权。</p>
<p>Linus 可以向 BitMover 公司道个歉，保证以后严格管教弟兄们，嗯，这是不可能的。实际情况是这样的：</p>
<p>Linus 花了两周时间自己用 C 写了一个分布式版本控制系统，这就是 Git！一个月之内，Linux 系统的源码已经由 Git 管理了！牛是怎么定义的呢？大家可以体会一下。</p>
<p>Git 迅速成为最流行的分布式版本控制系统，尤其是 2008 年，GitHub 网站上线了，它为开源项目免费提供 Git 存储，无数开源项目开始迁移至 GitHub，包括 jQuery，PHP，Ruby 等等。</p>
<p>历史就是这么偶然，如果不是当年 BitMover 公司威胁 Linux 社区，可能现在我们就没有免费而超级好用的 Git了。</p>
<h1 id="配置"><a href="#配置" class="headerlink" title="配置"></a>配置</h1><p>Git 的设置文件为 <code>.gitconfig</code> ，它可以在用户主目录下（全局配置），也可以在项目目录下（项目配置）。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">git config --list &#x2F;&#x2F;显示当前 Git 配置</span><br><span class="line">git config -e [--global] &#x2F;&#x2F;编辑 Git 配置，vim 编辑，中括号[--global]表示可加可不加，加了的话表示全局</span><br><span class="line">git config [--global] user.name &quot;[name]&quot; &#x2F;&#x2F;设置提交代码的用户信息</span><br><span class="line">git config [--global] user.email &quot;[email address]&quot;</span><br></pre></td></tr></table></figure>

<h1 id="创建版本仓库"><a href="#创建版本仓库" class="headerlink" title="创建版本仓库"></a>创建版本仓库</h1><ol>
<li>版本库又名仓库，英文名 <code>repository</code> 。选择一个合适的地方，创建一个空目录。</li>
<li>通过 <code>git init</code> 命令把这个目录变成 Git 可以管理的仓库（初始化仓库）。</li>
</ol>
<h1 id="把文件添加到版本库"><a href="#把文件添加到版本库" class="headerlink" title="把文件添加到版本库"></a>把文件添加到版本库</h1><ol>
<li>使用命令 <code>git add &lt;file&gt;</code> 告诉 Git，把文件 添加 到仓库。</li>
<li>使用命令 <code>git commit -m &quot;XXX&quot;</code> 告诉 Git，把文件 提交 到仓库， <code>-m</code> 后面跟的是本次提交的注释说明。<code>commit</code> 一次可以提交很多文件，可以使用多次 <code>add</code> 不同的文件。</li>
</ol>
<h1 id="commit-规范"><a href="#commit-规范" class="headerlink" title="commit 规范"></a>commit 规范</h1><p>一般情况下，提交 Git 时的注释可以分成几类，可以用几个动词开始：</p>
<ul>
<li>Added ( 新加入的需求 )</li>
<li>Fixed ( 修复 bug )</li>
<li>Changed ( 完成的任务 )</li>
<li>Updated ( 完成的任务，或者由于第三方模块变化而做的变化 )</li>
</ul>
<p>尽量将注释缩减为一句话，不要包含详细的内容。</p>
<h1 id="查看仓库的当前状态"><a href="#查看仓库的当前状态" class="headerlink" title="查看仓库的当前状态"></a>查看仓库的当前状态</h1><p> <code>git status</code> 命令可以查看仓库当前的状态，随时掌握工作区的状态，谁被修改过了，还没有提交等等。</p>
<p>修改文件内容，未提交到暂存区，此时输入 <code>git status</code> 后：</p>
<p><img src="https://gitee.com/guopengsong1/image-url/raw/master/20200823154907.png" alt="图示"></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">changes not staged for commit    &#x2F;&#x2F;改变 没有 进行 提交</span><br></pre></td></tr></table></figure>

<blockquote>
<p>上面的命令告诉我们，readme.txt 被修改过了，但是还没有准备提交(暂存)。</p>
</blockquote>
<p>将修改后的文件通过 <code>git add</code> 命令提交到暂存区后，此时输入 <code>git status</code> 后：</p>
<p><img src="https://gitee.com/guopengsong1/image-url/raw/master/20200823155001.png" alt="图示"></p>
<blockquote>
<p>上面的命令告诉我们，将要被提交的修改包括 readme.txt</p>
</blockquote>
<p>将文件通过 <code>git commit</code> 命令提交到仓库后 ，此时输入 <code>git status</code> 后：</p>
<p><img src="https://gitee.com/guopengsong1/image-url/raw/master/20200823155315.png" alt="图示"></p>
<blockquote>
<p>上述命令告诉我们当前没有需要提交的修改，而且工作目录是干净的。</p>
</blockquote>
<p> <code>git diff</code> 命令可以看到工作区和暂存区的区别； <code>git diff head -- readme.txt</code> 命令可以查看工作区和版本库里面的区别。</p>
<p><img src="https://gitee.com/guopengsong1/image-url/raw/master/20200823155341.png" alt="图示"></p>
<h1 id="查看提交的历史记录"><a href="#查看提交的历史记录" class="headerlink" title="查看提交的历史记录"></a>查看提交的历史记录</h1><p><code>git log</code> 命令可以查看所有提交的比较详细的历史纪录。<br>如果不想要太详细，可以使用 <code>git log --pretty=oneline</code>。</p>
<h1 id="时光穿梭机"><a href="#时光穿梭机" class="headerlink" title="时光穿梭机"></a>时光穿梭机</h1><p><code>6280d3……e547</code> 是 <code>commit id</code> （版本号）。<br>在 Git 中，用 <code>HEAD</code> 表示当前版本，上一个版本就是 <code>HEAD^</code> ，上上个版本就是 <code>HEAD^^</code> ，往前一百个版本为 <code>HEAD~100</code>。</p>
<p>回到上一个版本为 <code>git reset --hard &quot;HEAD^&quot;</code> (实测不区分大小写)</p>
<p>回到上上个版本为 <code>git reset --hard &quot;head^^&quot;</code> (实测不区分大小写)</p>
<p>假如知道版本号，回到某一个版本为 <code>git reset --hard 126378</code> (版本号,写前几位就行)</p>
<p>假如因为一些原因，关闭了命令行窗口，暂时无法知道某一次代码的版本号，可以使用命令 <code>git reflog</code> ，这个命令记录了你之前操作的每一次命令，可以查看版本号，知道了版本号就可以随意回退版本了。</p>
<p><img src="https://gitee.com/guopengsong1/image-url/raw/master/20200823155416.png" alt="图示"></p>
<h1 id="工作区和暂存区"><a href="#工作区和暂存区" class="headerlink" title="工作区和暂存区"></a>工作区和暂存区</h1><p>Git 和其他版本控制系统如 SVN 的一个不同之处就是有暂存区的概念。</p>
<p>工作区（Working Directory）：就是你电脑里面能看到的目录。<br>版本库（Repository）：工作区有一个隐藏目录 .git ，这个不算工作区，而是 Git 的版本库。Git 的版本库里存了很多东西，其中最重要的是称为 stage（或者叫做 index）的暂存区，还有 Git为我们自动创建的第一个分支 master，以及指向 master 的一个指针叫做 HEAD。</p>
<p>把文件往 Git 版本库里面添加的时候，是分两步执行的：</p>
<ol>
<li>用 <code>git add</code> 把文件添加进去，实际上就是把文件添加到暂存区；</li>
<li>用 <code>git commit</code> 提交更改，实际上就是把暂存区的所有内容提交到当前分支。</li>
</ol>
<p>因为我们创建 Git 版本库时候，Git 自动为我们创建了唯一一个 <code>master</code> 分支，所以，现在， <code>git commit</code> 就是往 <code>master</code> 分支上提交更改。</p>
<p><code>git commit</code> 只负责把暂存区的修改提交到仓库，不管工作区的内容。</p>
<p><img src="https://gitee.com/guopengsong1/image-url/raw/master/20200823155443.png" alt="图示"></p>
<h1 id="撤销修改"><a href="#撤销修改" class="headerlink" title="撤销修改"></a>撤销修改</h1><p><code>git checkout -- file</code> 可以丢弃工作区的修改：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git checkout -- readme.txt</span><br></pre></td></tr></table></figure>

<p><img src="https://gitee.com/guopengsong1/image-url/raw/master/20200823155518.png" alt="图示"></p>
<p>命令 <code>git checkout -- readme.txt</code> 意思就是，把 readme.txt 文件在工作区的修改全部撤销，这里有两种情况：</p>
<ol>
<li>一种是 readme.txt 自修改后还 没有被放到暂存区，现在，撤销修改就回到和版本库一模一样的状态。</li>
<li>一种是 readme.txt 已经 添加到暂存区后，又做了修改，现在，撤销修改就回到添加到暂存区后的状态。</li>
</ol>
<p><code>git checkout -- file</code> 命令中的 <code>--</code> 很重要，没有 <code>--</code> ，就变成了“切换到另一个分支”的命令，我们在后面的分支管理中会再次遇到 <code>git checkout</code> 命令。</p>
<p>假如修改的内容已经被 <code>git add</code> 到了暂存区，可以使用 <code>git reset HEAD file</code> 可以把暂存区的修改撤销掉，重新放回工作区。</p>
<p><img src="https://gitee.com/guopengsong1/image-url/raw/master/20200823155547.png" alt="图示"></p>
<p><code>git reset</code> 命令可以回退版本，也可以把暂存区的修改退回到工作区。当我们用 <code>HEAD</code> 时，表示最新的版本。</p>
<p>再用 <code>git status</code> 查看一下，发现暂存区是干净的，工作区又修改。</p>
<p>然后再使用 <code>git checkout -- file</code>，撤销掉工作区的操作。</p>
<p>假如修改的内容从暂存区 <code>git commit</code> 到了版本库，回退的话可以参照时光穿梭机，会回退到上一版本， <code>git reset --hard &quot;HEAD^&quot;</code> 。（不过这是有条件的，就是此时本地的代码没有被推送到远程仓库）</p>
<h1 id="远程仓库"><a href="#远程仓库" class="headerlink" title="远程仓库"></a>远程仓库</h1><p>Git 是分布式版本管理系统，同一个 Git 仓库，可以分布到不同的机器上。怎么分布呢？最早，肯定只有一台机器有一个原始版本库，此后，别的机器可以克隆这个原始版本库，而且每台机器的版本库其实都是一样的，并没有主次之分。</p>
<p>实际情况是这样的，找一台电脑充当服务器的角色，每天24小时开机，其他每个人都从这个“服务器”仓库克隆一份到自己的电脑上，并且各自把各自的提交推送到服务器仓库里，也从服务器仓库中拉取别人的提交。GitHub 就类似于“服务器”的角色，托管代码。</p>
<h1 id="查看远程仓库"><a href="#查看远程仓库" class="headerlink" title="查看远程仓库"></a>查看远程仓库</h1><p>查看远程分支： <code>git branch -a</code><br>==注意：这条命令并没有每一次都从远程更新仓库信息，这样子做是为了效率，== 我们可以手动更新一下远程分支：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">git fetch origin    &#x2F;&#x2F;更新远程仓库</span><br><span class="line">git branch -a    &#x2F;&#x2F;查看远程分支</span><br></pre></td></tr></table></figure>

<h1 id="拉取远程分支、并创建本地分支"><a href="#拉取远程分支、并创建本地分支" class="headerlink" title="拉取远程分支、并创建本地分支"></a>拉取远程分支、并创建本地分支</h1><p>方法一：<code>git checkout -b 本地分支 XXX origin/远程分支XXX</code></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git checkout -b gps origin&#x2F;gps    &#x2F;&#x2F;将远程的分支 gps 拉取到本地 gps 分支，并且切换到本地 gps 分支</span><br></pre></td></tr></table></figure>

<p>使用该方式会在本地新建分支 gps，并自动切换到该本地分支 gps。</p>
<p>方法二：<code>git fetch origin 远程分支XXX:本地分支XXX</code></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git fetch origin gps:gps    &#x2F;&#x2F;将远程的分支 gps 拉取到本地 gps 分支，但是不会自动切换到本地的 gps 分支</span><br></pre></td></tr></table></figure>

<p>使用该方式会在本地新建分支 gps，但是不会自动切换到该本地分支 gps，需要手动 <code>checkout</code>。</p>
<h1 id="添加远程仓库"><a href="#添加远程仓库" class="headerlink" title="添加远程仓库"></a>添加远程仓库</h1><p>远程仓库建立的方法略过，假定现在已经有了线上的远程仓库地址。<br>在本地的 learngit 仓库下运行命令：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git remote add origin git@github.com:albert-guo&#x2F;learngit.git</span><br></pre></td></tr></table></figure>

<p>上面的 albert-guo 替换成自己的 GitHub 帐户名，否则你在本地关联的就不一定是你的仓库，关联错了也没关系，但是以后是无法推上去的，因为你的 <code>SSH Key</code> 公钥不在这个帐户的列表中。</p>
<p>添加后，远程仓库的名字就是 <code>origin</code> ，这是 Git 默认的叫法，也可以改成别的，但是 <code>origin</code> 这个名字一看就知道是远程仓库。</p>
<p>把本地库的所有内容推送到远程仓库上：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git push -u origin master</span><br></pre></td></tr></table></figure>

<p>把本地库的内容推送到远程，用 <code>git push</code> 命令，实际上是把当前分支 <code>master</code> 推送到远程。</p>
<p>由于远程仓库是空的，我们第一次推送的 <code>master</code>  分支时候，加上了 <code>-u</code> 参数，Git 不但会把本地的 <code>master</code>  分支推送到远程新的 <code>master</code> 分支，还会把本地的 <code>master</code> 分支和远程的 <code>master</code>  分支关联起来，在以后的推送或者拉取的时候可以简化命令。</p>
<p>从现在起，只要本地做了提交，就可以通过命令：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git push origin master</span><br></pre></td></tr></table></figure>

<p>把本地 <code>master</code> 分支的最新修改推送到 GitHub，现在就拥有了真正的分布式版本库。</p>
<p>分布式版本系统最大的好处之一就是在本地工作的时候不需要考虑远程仓库的存在，也就是没有联网也可以工作，而 svn 在没有联网的时候是拒绝干活的，当有网络的时候再把本地提交推送一下就完成了同步，比较方便。</p>
<h1 id="从远程仓库克隆"><a href="#从远程仓库克隆" class="headerlink" title="从远程仓库克隆"></a>从远程仓库克隆</h1><p>要克隆一个仓库，首先必须知道仓库地址，然后使用 <code>git clone</code> 命令克隆。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git clone git@github.com:albert-guo&#x2F;learngit.git</span><br></pre></td></tr></table></figure>

<p>Git 支持多种协议，包括　<code>https</code> ，但是通过 <code>ssh</code> 支持的原生 Git 协议最快。默认的　<code>git://</code>　使用 <code>ssh</code>，但也可以使用 <code>https</code> 等其他协议。使用 <code>https</code> 除了速度慢以外，还有个最大的麻烦就是每次推送必须输入口令，只是在某些只开放 http 端口的公司内部就无法使用 ssh 协议而只能用 <code>https</code>。</p>
<h1 id="创建与合并分支"><a href="#创建与合并分支" class="headerlink" title="创建与合并分支"></a>创建与合并分支</h1><p>首先创建 dev 分支，然后切换到 dev 分支。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git checkout -b dev</span><br></pre></td></tr></table></figure>

<p><code>git checkout</code> 命令上加 <code>-b</code> 参数表示创建并且切换，相当于下面两条命令。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">git branch dev &#x2F;&#x2F; 创建一个叫 dev 的分支。</span><br><span class="line">git checkout dev &#x2F;&#x2F; 切换到 dev 分支。</span><br></pre></td></tr></table></figure>

<p>查看当前所在分支命令 <code>git branch</code> 。该命令会列出所有分支，并且在当前的分支前面加 <code>*</code> 。</p>
<p><img src="https://gitee.com/guopengsong1/image-url/raw/master/20200823155619.png" alt="图示"></p>
<p>在 dev 分支上提交几次代码后，尝试将 dev 分支和 master 分支合并。</p>
<p><code>git merge</code> 命令用于合并指定分支到当前分支。比如当前在 master 分支上，然后通过命令 <code>git merge dev</code> 可将 dev 分支合并到当前的 master 分支。</p>
<p><code>git merge --abort</code> 表示撤销刚才的合并。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">git merge dev   &#x2F;&#x2F; 将 dev 分支合并到当前的 merge 分支上</span><br><span class="line">git merge --abort    &#x2F;&#x2F; 将刚才合并操作撤销</span><br></pre></td></tr></table></figure>

<p><img src="https://gitee.com/guopengsong1/image-url/raw/master/20200823155651.png" alt="图示"></p>
<p>注意上面的 <code>Fast-forward</code> 信息，表示这次合并是“快进模式”，也就是直接把 master 指向 dev 的当前提交，所以合并速度非常快。当然也不是每一次合并都能 <code>Fast-forward</code> ，后续还会有其他方式合并。</p>
<p>合并之后，可以放心删除 dev 分支。删除命令 <code>git branch -d dev</code> 。</p>
<p>小结：</p>
<ul>
<li>查看分支：<code>git branch</code></li>
<li>创建分支：<code>git branch &lt;name&gt;</code></li>
<li>切换分支：<code>git checkout &lt;name&gt;</code></li>
<li>创建+切换分支：<code>git checkout -b &lt;name&gt;</code></li>
<li>合并某分支到当前分支：<code>git merge &lt;name&gt;</code></li>
<li>删除分支：<code>git branch -d &lt;name&gt;</code></li>
<li>强制删除分支（没有合并）：<code>git branch -D &lt;name&gt;</code></li>
</ul>
<h1 id="解决冲突"><a href="#解决冲突" class="headerlink" title="解决冲突"></a>解决冲突</h1><p>假如两个分支编辑的内容区块相同，各分支都有了自己新的提交。这种情况下，Git 无法执行“快速合并”，只能试图把各自的修改合并起来，但是这种合并就会有冲突。</p>
<p><img src="https://gitee.com/guopengsong1/image-url/raw/master/20200823155729.png" alt="图示"></p>
<p>Git 告诉我们，合并失败，必须手动解决冲突后再提交。<br>此时，用命令 <code>git diff</code> 可以查看冲突的地方。也可以直接在冲突的文件里，找到相关的冲突的位置。</p>
<p>Git 会用 <code>&lt;&lt;&lt;&lt;&lt;&lt;&lt; , ======= , &gt;&gt;&gt;&gt;&gt;&gt;&gt;</code> 标记出不同分支的内容，然后在文件中解决了冲突后保存。<br>用带参数的 <code>git log --graph --pretty=oneline</code> ，简短显示分支的合并情况。<br><code>git log --graph</code> 命令可以看到详细分支合并图，<code>git log --graph --pretty=oneline</code> 命令可以简单看到分支合并情况。</p>
<p><img src="https://gitee.com/guopengsong1/image-url/raw/master/20200823155812.png" alt="图示"></p>
<p>解决完冲突后，可将 dev 分支删除，命令为 <code>git branch -d dev</code> 。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git branch -d dev   &#x2F;&#x2F;删除 dev 分支</span><br></pre></td></tr></table></figure>

<h1 id="禁用快速合并"><a href="#禁用快速合并" class="headerlink" title="禁用快速合并"></a>禁用快速合并</h1><p>通常情况下，合并分支时，如果可能，Git 会用 Fast Forward 模式，但是这种模式下，删除分支后，会丢掉分支信息。<br>如果要强制禁用 Fast Forward 模式，Git 就会在 merge 时生成一个新的 commit ，这样，从分支历史上就可以看出分支信息。</p>
<p>比如我们从现在在 master 分支上，要把 dev 分支合并到当前的 master 分支。<br>可以用 <code>git merge --no-ff -m</code>  “强制合并，禁用快速合并” dev。<br>因为本次合并要创建一个 commit ，所以加了一个 -m 参数，把 commit 描述进去。</p>
<h1 id="gitignore"><a href="#gitignore" class="headerlink" title="gitignore"></a>gitignore</h1><p>一定要养成在项目开始就创建 <code>.gitignore</code> 文件的习惯，否则一旦 push，处理起来会非常麻烦。</p>
<p>配置语法：</p>
<ul>
<li>以 <code>/</code> 开头表示根目录,防止递归</li>
<li>以 <code>/</code> 结尾表示指定目录</li>
<li>以 <code>!</code> 开头表示不过滤（跟踪）此项配置匹配到的文件或目录</li>
<li>以 <code>#</code> 开头表示注释，如需转义在前面加斜杠，/#</li>
<li>配置规则可以使用标准的 glob 模式匹配(glob 模式是指 shell 所使用的简化了的正则表达式)</li>
</ul>
<p>有时候在项目开发过程中，突然心血来潮想把某些目录或文件加入忽略规则，按照上述方法定义后发现并未生效，原因是 <code>.gitignore</code> 只能忽略那些原来没有被 <code>track</code> 的文件，如果某些文件已经被纳入了版本管理中，则修改 <code>.gitignore</code> 是无效的。那么解决方法就是先把本地缓存删除（改变成未 <code>track</code> 状态），然后再提交：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">git rm -r --cached .</span><br><span class="line">git add .</span><br><span class="line">git commit -m &#39;update .gitignore&#39;</span><br></pre></td></tr></table></figure>
      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://yoursite.com/2018/02/10/%E9%80%9A%E4%BF%97%E7%90%86%E8%A7%A3-http-%E4%B8%8E-https/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="https://inews.gtimg.com/newsapp_ls/0/12122532695/0">
      <meta itemprop="name" content="Albert Guo">
      <meta itemprop="description" content="郭鹏松的个人博客">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Albert Guo">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/2018/02/10/%E9%80%9A%E4%BF%97%E7%90%86%E8%A7%A3-http-%E4%B8%8E-https/" class="post-title-link" itemprop="url">通俗理解 http 与 https</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2018-02-10 22:35:24" itemprop="dateCreated datePublished" datetime="2018-02-10T22:35:24+08:00">2018-02-10</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2020-08-24 20:44:26" itemprop="dateModified" datetime="2020-08-24T20:44:26+08:00">2020-08-24</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/%E6%B5%8F%E8%A7%88%E5%99%A8/" itemprop="url" rel="index"><span itemprop="name">浏览器</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <blockquote>
<p>最近在 B 站上看了 <strong>啃芝士</strong> 老师分享的 <code>http</code> 和 <code>https</code> 视频教程。视频简短精悍，通俗易懂的阐明了两者的关系，所以整理了一下笔记，用于学习帮助理解。<br>原视频地址：<a href="https://www.bilibili.com/video/BV1Tx411s7jZ?from=search&amp;seid=11397787170634668577" target="_blank" rel="noopener">https://www.bilibili.com/video/BV1Tx411s7jZ?from=search&amp;seid=11397787170634668577</a></p>
</blockquote>
<h1 id="http"><a href="#http" class="headerlink" title="http"></a>http</h1><p>HTTP 协议（HyperText Transfer Protocol，超文本传输协议）是因特网上应用最为广泛的一种网络传输协议，所有的 WWW 文件都必须遵守这个标准。</p>
<p>访问 <code>http</code> 网站，往往采用明文的方式向服务器发送内容，没有任何加密。如果用户发送了一些账号密码等隐私信息的话，很容易中间被爬虫劫持。</p>
<p><img src="https://gitee.com/guopengsong1/image-url/raw/master/20200824202657.gif" alt="爬虫劫持"></p>
<h1 id="https"><a href="#https" class="headerlink" title="https"></a>https</h1><p>HTTPS 是一种通过计算机网络进行安全通信的传输协议，经由 HTTP 进行通信，利用 <code>SSL/TLS</code> 建立全信道，加密数据包。<code>HTTPS</code> 使用的主要目的是提供对网站服务器的身份认证，同时保护交换数据的隐私与完整性。</p>
<p>访问 <code>https</code> 的网站，首先浏览器会和服务器建立一个安全的链接通道。然后服务器会发送一份网站的证书信息到客户端，相当于告诉客户端，你访问的服务器没有问题。</p>
<p><img src="https://gitee.com/guopengsong1/image-url/raw/master/20200824203206.gif" alt="客户端接收证书，确认访问的服务器没有问题"></p>
<p>确认了信息之后，服务器端会生成一个加锁的箱子，但是这个加锁的箱子，有两把不一样的钥匙。一把是给服务器自己的，一把是给客户端的。然后服务器会把没有上锁的箱子和钥匙发送给客户端。</p>
<p><img src="https://gitee.com/guopengsong1/image-url/raw/master/20200824203450.gif" alt="客户端接收钥匙和箱子"></p>
<p>客户端将需要发送的文件放在箱子中。然后用钥匙锁上，再发送给服务端。</p>
<p><img src="https://gitee.com/guopengsong1/image-url/raw/master/20200824204046.gif" alt="客户端锁上箱子后发送给服务端"></p>
<p>服务器接收了客户端发送的信息后，再用自己的钥匙打开箱子，来保证信息的安全。</p>
<p>在这个过程中，及时信息被拦截了，因为没有服务器的钥匙，以目前的技术来讲，很难去打开箱子破解。</p>
<p>所以一些大型网站，尤其是购物、登录、注册等网站，都采用了 <code>https</code> 协议进行传输。</p>

      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://yoursite.com/2017/12/15/%E8%80%81%E7%94%9F%E5%B8%B8%E8%B0%88%E2%80%94%E2%80%94call-apply-bind/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="https://inews.gtimg.com/newsapp_ls/0/12122532695/0">
      <meta itemprop="name" content="Albert Guo">
      <meta itemprop="description" content="郭鹏松的个人博客">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Albert Guo">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/2017/12/15/%E8%80%81%E7%94%9F%E5%B8%B8%E8%B0%88%E2%80%94%E2%80%94call-apply-bind/" class="post-title-link" itemprop="url">老生常谈——call,apply,bind</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2017-12-15 19:41:39" itemprop="dateCreated datePublished" datetime="2017-12-15T19:41:39+08:00">2017-12-15</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2020-07-15 19:55:23" itemprop="dateModified" datetime="2020-07-15T19:55:23+08:00">2020-07-15</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/JavaScript/" itemprop="url" rel="index"><span itemprop="name">JavaScript</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <blockquote>
<p>本文部分摘录自 <strong>JavaScript官方教程</strong><br>参考地址：<a href="https://wangdoc.com/javascript/oop/this.html#functionprototypebind" target="_blank" rel="noopener">https://wangdoc.com/javascript/oop/this.html#functionprototypebind</a>   </p>
</blockquote>
<p>最近接触了 React ，里面的方法需要绑定 <code>this</code> 的指向问题，官方推荐用 <code>bind</code> 进行绑定。所以整理学习了一下老生常谈的问题，JavaScript 的 <code>this</code> 的指向和绑定。</p>
<h2 id="this-关键字"><a href="#this-关键字" class="headerlink" title="this 关键字"></a>this 关键字</h2><p> <code>this</code> 就是属性或方法“当前”所在的对象。不管是什么场合，<code>this</code> 都有一个共同点：它总是返回一个对象。</p>
<p> 在 JavaScript 语言中，一切皆对象，运行环境也是对象，所以函数都是在某个对象之中运行，<code>this</code> 就是函数运行时所在的对象（环境）。这本来不会让用户糊涂，但是 JavaScript 支持运行环境动态切换，也就是说，<code>this</code> 的指向时动态的，没办法事先确定到底指向哪个对象，这才是让人困惑的地方。</p>
 <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">function f() &#123;</span><br><span class="line">  return &#39;姓名：&#39;+ this.name;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">var A &#x3D; &#123;</span><br><span class="line">  name: &#39;张三&#39;,</span><br><span class="line">  describe: f</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">var B &#x3D; &#123;</span><br><span class="line">  name: &#39;李四&#39;,</span><br><span class="line">  describe: f</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">A.describe() &#x2F;&#x2F; &quot;姓名：张三&quot;</span><br><span class="line">B.describe() &#x2F;&#x2F; &quot;姓名：李四&quot;</span><br></pre></td></tr></table></figure>

<p> <code>this</code> 使用场合：</p>
<ul>
<li><p>全局环境：全局环境使用 <code>this</code> ，它指向的就是顶层对象 <code>window</code>。</p>
</li>
<li><p>构造函数：构造函数中的 <code>this</code> ，指的就是实例对象。</p>
</li>
<li><p>对象的方法：如果对象的方法里面包含 <code>this</code> , <code>this</code> 的指向就是方法所运行时所在的对象。该方法赋值给另一个对象，就会改变 this 的指向。</p>
<p><code>this</code> 使用注意点：</p>
</li>
<li><p>避免多层 <code>this</code>：由于 <code>this</code> 的指向是不确定的，避免在函数中包含多层 <code>this</code>。</p>
</li>
<li><p>避免数组方法中 <code>this</code>：数组的 <code>map</code> 和 <code>foreach</code> 方法，允许提供一个函数作为参数。这个函数内部不应该使用 <code>this</code>。</p>
</li>
<li><p>避免回调函数中的 <code>this</code>：回调函数中的 <code>this</code> 往往会改变指向，最好避免使用。</p>
<h3 id="绑定-this-的方法"><a href="#绑定-this-的方法" class="headerlink" title="绑定 this 的方法"></a>绑定 this 的方法</h3><p>JavaScript 提供了 <code>call、apply、bind</code> 这三个方法，来切换/固定 <code>this</code> 的指向。</p>
<h4 id="Function-prototype-call"><a href="#Function-prototype-call" class="headerlink" title="Function.prototype.call()"></a>Function.prototype.call()</h4><p>函数实例的 <code>call</code> 方法，可以指定函数内部的 <code>this</code>  指向（即函数执行时所在的作用域），然后在指定的作用域中，调用该函数。</p>
<p><code>call</code> 方法的参数，应该是一个对象。如果参数为 <code>空、null</code> 和 <code>undefined</code> ，则默认传入全局对象。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">var obj &#x3D; &#123;&#125;;</span><br><span class="line"></span><br><span class="line">var f &#x3D; function () &#123;</span><br><span class="line">  return this;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">f() &#x3D;&#x3D;&#x3D; window &#x2F;&#x2F; true</span><br><span class="line">f.call(obj) &#x3D;&#x3D;&#x3D; obj &#x2F;&#x2F; true</span><br></pre></td></tr></table></figure>

<p><code>call</code> 方法还可以接受多个参数。<code>call</code> 的第一个参数就是 <code>this</code> 所要指向的那个对象，后面的参数则是函数调用时所需的参数。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">func.call(thisValue, arg1, arg2, ...)</span><br></pre></td></tr></table></figure>

<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">function add(a, b) &#123;</span><br><span class="line">  return a + b;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">add.call(this, 1, 2) &#x2F;&#x2F; 3</span><br></pre></td></tr></table></figure>

<h4 id="Function-prototype-apply"><a href="#Function-prototype-apply" class="headerlink" title="Function.prototype.apply()"></a>Function.prototype.apply()</h4><p><code>apply</code> 方法的作用与 <code>call</code> 方法类似，也是改变 <code>this</code> 指向，然后再调用该函数。唯一的区别就是，它接收一个数组作为函数执行时的参数。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">func.apply(thisValue, [arg1, arg2, ...])</span><br></pre></td></tr></table></figure>

<h4 id="Function-prototype-bind"><a href="#Function-prototype-bind" class="headerlink" title="Function.prototype.bind()"></a>Function.prototype.bind()</h4><p><code>bind</code> 方法用于将函数体内的 <code>this</code> 绑定到某个对象，然后返回一个新函数。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">var d &#x3D; new Date();</span><br><span class="line">d.getTime() &#x2F;&#x2F; 1481869925657</span><br><span class="line"></span><br><span class="line">var print &#x3D; d.getTime;</span><br><span class="line">print() &#x2F;&#x2F; Uncaught TypeError: this is not a Date object.</span><br></pre></td></tr></table></figure>

<p>上面代码中，我们将 <code>d.getTime()</code> 方法赋给变量 <code>print</code>，然后调用 <code>print()</code> 就报错了。这是因为 <code>getTime()</code> 方法内部的 <code>this</code>，绑定 <code>Date</code> 对象的实例，赋给变量 <code>print</code> 以后，内部的 <code>this</code> 已经不指向 <code>Date</code> 对象的实例了。</p>
<p><code>bind()</code> 方法可以解决这个问题。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">var print &#x3D; d.getTime.bind(d);</span><br><span class="line">print() &#x2F;&#x2F; 1481869925657</span><br></pre></td></tr></table></figure>

<p>上面代码中，<code>bind()</code> 方法将 <code>getTime()</code> 方法内部的 <code>this</code> 绑定到 <code>d</code> 对象，这时就可以安全地将这个方法赋值给其他变量了。</p>
<p><code>bind()</code> 的作用与 <code>call()</code> 和 <code>apply()</code> 一样，都是可以改变函数运行时上下文，区别是 <code>call()</code> 和 <code>apply()</code> 在调用函数之后会立即执行，而 <code>bind()</code> 方法调用并改变函数运行时上下文后，返回一个新的函数，供我们需要时再调用。</p>
<h3 id="如何选用"><a href="#如何选用" class="headerlink" title="如何选用"></a>如何选用</h3></li>
<li><p>如果不需要关心具体有多少参数被传入函数，选用 <code>apply()</code>；</p>
</li>
<li><p>如果确定函数可接收多少个参数，并且想一目了然表达形参和实参的对应关系，用 <code>call()</code>；</p>
</li>
<li><p>如果我们想要将来再调用方法，不需立即得到函数返回结果，则使用 <code>bind()</code>。</p>
<h3 id="特点总结"><a href="#特点总结" class="headerlink" title="特点总结"></a>特点总结</h3></li>
<li><p><code>call()</code>、<code>apply()</code> 和 <code>bind()</code> 都是用来改变函数执行时的上下文，可借助它们实现继承；</p>
</li>
<li><p><code>call()</code> 和 <code>apply()</code> 唯一区别是参数不一样，<code>call()</code> 是 <code>apply()</code> 的语法糖；</p>
</li>
<li><p><code>bind()</code> 是返回一个新函数，供以后调用，而 <code>apply()</code> 和 <code>call()</code> 是立即调用。</p>
</li>
</ul>

      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://yoursite.com/2017/06/12/JavaScript-%E8%BF%90%E8%A1%8C%E6%9C%BA%E5%88%B6%E8%AF%A6%E8%A7%A3/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="https://inews.gtimg.com/newsapp_ls/0/12122532695/0">
      <meta itemprop="name" content="Albert Guo">
      <meta itemprop="description" content="郭鹏松的个人博客">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Albert Guo">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/2017/06/12/JavaScript-%E8%BF%90%E8%A1%8C%E6%9C%BA%E5%88%B6%E8%AF%A6%E8%A7%A3/" class="post-title-link" itemprop="url">JavaScript 运行机制详解</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2017-06-12 20:58:20" itemprop="dateCreated datePublished" datetime="2017-06-12T20:58:20+08:00">2017-06-12</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2020-07-20 21:05:32" itemprop="dateModified" datetime="2020-07-20T21:05:32+08:00">2020-07-20</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/JavaScript/" itemprop="url" rel="index"><span itemprop="name">JavaScript</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <blockquote>
<p>本文摘抄自 <strong>阮一峰</strong> 老师博客，仅供学习参考。<br>原文地址：<a href="http://www.ruanyifeng.com/blog/2014/10/event-loop.html" target="_blank" rel="noopener">http://www.ruanyifeng.com/blog/2014/10/event-loop.html</a></p>
</blockquote>
<h1 id="一、为什么JavaScript是单线程？"><a href="#一、为什么JavaScript是单线程？" class="headerlink" title="一、为什么JavaScript是单线程？"></a>一、为什么JavaScript是单线程？</h1><p>JavaScript 语言的一大特点就是单线程，也就是说，同一个时间只能做一件事。那么，为什么 JavaScript 不能有多个线程呢？这样能提高效率啊。</p>
<p>JavaScript 的单线程，与它的用途有关。作为浏览器脚本语言，JavaScript 的主要用途是与用户互动，以及操作 DOM。这决定了它只能是单线程，否则会带来很复杂的同步问题。比如，假定 JavaScript 同时有两个线程，一个线程在某个 DOM 节点上添加内容，另一个线程删除了这个节点，这时浏览器应该以哪个线程为准？</p>
<p>所以，为了避免复杂性，从一诞生，JavaScript 就是单线程，这已经成了这门语言的核心特征，将来也不会改变。</p>
<p>为了利用多核 CPU 的计算能力，HTML5 提出 Web Worker 标准，允许 JavaScript 脚本创建多个线程，但是子线程完全受主线程控制，且不得操作 DOM。所以，这个新标准并没有改变 JavaScript 单线程的本质。</p>
<h1 id="二、任务队列"><a href="#二、任务队列" class="headerlink" title="二、任务队列"></a>二、任务队列</h1><p>单线程就意味着，所有任务需要排队，前一个任务结束，才会执行后一个任务。如果前一个任务耗时很长，后一个任务就不得不一直等着。</p>
<p>如果排队是因为计算量大，CPU 忙不过来，倒也算了，但是很多时候 CPU 是闲着的，因为 IO 设备（输入输出设备）很慢（比如 Ajax 操作从网络读取数据），不得不等着结果出来，再往下执行。</p>
<p>JavaScript 语言的设计者意识到，这时主线程完全可以不管 IO 设备，挂起处于等待中的任务，先运行排在后面的任务。等到 IO 设备返回了结果，再回过头，把挂起的任务继续执行下去。</p>
<p>于是，==所有任务可以分成两种，一种是同步任务（synchronous），另一种是异步任务（asynchronous）。== 同步任务指的是，在主线程上排队执行的任务，只有前一个任务执行完毕，才能执行后一个任务；异步任务指的是，不进入主线程、而进入”任务队列”（task queue）的任务，只有”任务队列”通知主线程，某个异步任务可以执行了，该任务才会进入主线程执行。</p>
<p>具体来说，异步执行的运行机制如下。（同步执行也是如此，因为它可以被视为没有异步任务的异步执行。）</p>
<ol>
<li>所有同步任务都在主线程上执行，形成一个执行栈（execution context stack）。</li>
<li>主线程之外，还存在一个”任务队列”（task queue）。只要异步任务有了运行结果，就在”任务队列”之中放置一个事件。</li>
<li>一旦”执行栈”中的所有同步任务执行完毕，系统就会读取”任务队列”，看看里面有哪些事件。那些对应的异步任务，于是结束等待状态，进入执行栈，开始执行。</li>
<li>主线程不断重复上面的第三步。</li>
</ol>
<p>下图就是主线程和任务队列的示意图。</p>
<p><img src="https://note.youdao.com/yws/public/resource/a71ec9ee838e17ff2e49dbcc7451790f/xmlnote/WEBRESOURCE21c14910a081cf794567059dd18e247f/5696" alt="主线程和任务队列示意图"></p>
<p>只要主线程空了，就会去读取”任务队列”，这就是 JavaScript 的运行机制。这个过程会不断重复。</p>
<h1 id="三、事件和回调函数"><a href="#三、事件和回调函数" class="headerlink" title="三、事件和回调函数"></a>三、事件和回调函数</h1><p>“任务队列”是一个事件的队列（也可以理解成消息的队列），IO 设备完成一项任务，就在”任务队列”中添加一个事件，表示相关的异步任务可以进入”执行栈”了。主线程读取”任务队列”，就是读取里面有哪些事件。</p>
<p>“任务队列”中的事件，除了 IO 设备的事件以外，还包括一些用户产生的事件（比如鼠标点击、页面滚动等等）。只要指定过回调函数，这些事件发生时就会进入”任务队列”，等待主线程读取。</p>
<p>所谓”回调函数”（callback），就是那些会被主线程挂起来的代码。异步任务必须指定回调函数，当主线程开始执行异步任务，就是执行对应的回调函数。</p>
<p>“任务队列”是一个先进先出的数据结构，排在前面的事件，优先被主线程读取。主线程的读取过程基本上是自动的，只要执行栈一清空，”任务队列”上第一位的事件就自动进入主线程。但是，由于存在后文提到的”定时器”功能，主线程首先要检查一下执行时间，某些事件只有到了规定的时间，才能返回主线程。</p>
<h1 id="四、Event-Loop"><a href="#四、Event-Loop" class="headerlink" title="四、Event Loop"></a>四、Event Loop</h1><p>主线程从”任务队列”中读取事件，这个过程是循环不断的，所以整个的这种运行机制又称为 Event Loop（事件循环）。</p>
<p>为了更好地理解 Event Loop，请看下图（转引自Philip Roberts的演讲《Help, I’m stuck in an event-loop》）。</p>
<p><img src="https://note.youdao.com/yws/public/resource/a71ec9ee838e17ff2e49dbcc7451790f/xmlnote/WEBRESOURCE9b30fd1cb0660cbab875c88b3bd2d287/5698" alt="event-loop 示意图"></p>
<p>上图中，主线程运行的时候，产生堆（heap）和栈（stack），栈中的代码调用各种外部 API，它们在”任务队列”中加入各种事件（click，load，done）。只要栈中的代码执行完毕，主线程就会去读取”任务队列”，依次执行那些事件所对应的回调函数。</p>
<p>执行栈中的代码（同步任务），总是在读取”任务队列”（异步任务）之前执行。请看下面这个例子。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">var req &#x3D; new XMLHttpRequest();</span><br><span class="line">    req.open(&#39;GET&#39;, url);    </span><br><span class="line">    req.onload &#x3D; function ()&#123;&#125;;    </span><br><span class="line">    req.onerror &#x3D; function ()&#123;&#125;;    </span><br><span class="line">    req.send();</span><br></pre></td></tr></table></figure>

<p>上面代码中的 <code>req.send</code> 方法是 Ajax 操作向服务器发送数据，它是一个异步任务，意味着只有当前脚本的所有代码执行完，系统才会去读取”任务队列”。所以，它与下面的写法等价。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">var req &#x3D; new XMLHttpRequest();</span><br><span class="line">req.open(&#39;GET&#39;, url);</span><br><span class="line">req.send();</span><br><span class="line">req.onload &#x3D; function ()&#123;&#125;;    </span><br><span class="line">req.onerror &#x3D; function ()&#123;&#125;;</span><br></pre></td></tr></table></figure>

<p>也就是说，指定回调函数的部分（onload 和 onerror），在 <code>send()</code> 方法的前面或后面无关紧要，因为它们属于执行栈的一部分，系统总是执行完它们，才会去读取”任务队列”。</p>
<h1 id="五、定时器"><a href="#五、定时器" class="headerlink" title="五、定时器"></a>五、定时器</h1><p>除了放置异步任务的事件，”任务队列”还可以放置定时事件，即指定某些代码在多少时间之后执行。这叫做”定时器”（timer）功能，也就是定时执行的代码。</p>
<p>定时器功能主要由 <code>setTimeout()</code> 和 <code>setInterval()</code> 这两个函数来完成，它们的内部运行机制完全一样，区别在于前者指定的代码是一次性执行，后者则为反复执行。以下主要讨论 <code>setTimeout()</code>。</p>
<p><code>setTimeout()</code> 接受两个参数，第一个是回调函数，第二个是推迟执行的毫秒数。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">console.log(1);</span><br><span class="line">setTimeout(function()&#123;console.log(2);&#125;,1000);</span><br><span class="line">console.log(3);</span><br></pre></td></tr></table></figure>

<p>上面代码的执行结果是 <code>1，3，2</code>，因为 <code>setTimeout()</code> 将第二行推迟到 <code>1000</code> 毫秒之后执行。</p>
<p>如果将 <code>setTimeout()</code> 的第二个参数设为 <code>0</code>，就表示当前代码执行完（执行栈清空）以后，立即执行（<code>0</code> 毫秒间隔）指定的回调函数。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">setTimeout(function()&#123;console.log(1);&#125;, 0);</span><br><span class="line">console.log(2);</span><br></pre></td></tr></table></figure>

<p>上面代码的执行结果总是 <code>2，1</code>，因为只有在执行完第二行以后，系统才会去执行”任务队列”中的回调函数。</p>
<p>总之，<code>setTimeout(fn,0)</code> 的含义是，指定某个任务在主线程最早可得的空闲时间执行，也就是说，尽可能早得执行。它在”任务队列”的尾部添加一个事件，因此要等到同步任务和”任务队列”现有的事件都处理完，才会得到执行。</p>
<p>HTML5 标准规定了 <code>setTimeout()</code> 的第二个参数的最小值（最短间隔），不得低于 <code>4</code> 毫秒，如果低于这个值，就会自动增加。在此之前，老版本的浏览器都将最短间隔设为 <code>10</code> 毫秒。另外，对于那些 DOM 的变动（尤其是涉及页面重新渲染的部分），通常不会立即执行，而是每 <code>16</code> 毫秒执行一次。这时使用 <code>requestAnimationFrame()</code> 的效果要好于 <code>setTimeout()</code>。</p>
<p>需要注意的是，<code>setTimeout()</code> 只是将事件插入了”任务队列”，必须等到当前代码（执行栈）执行完，主线程才会去执行它指定的回调函数。要是当前代码耗时很长，有可能要等很久，所以并没有办法保证，回调函数一定会在 <code>setTimeout()</code> 指定的时间执行。</p>
<h1 id="六、Node-js的Event-Loop"><a href="#六、Node-js的Event-Loop" class="headerlink" title="六、Node.js的Event Loop"></a>六、Node.js的Event Loop</h1><p>Node.js 也是单线程的 Event Loop，但是它的运行机制不同于浏览器环境。</p>
<p>请看下面的示意图（作者@BusyRich）。</p>
<p><img src="https://note.youdao.com/yws/public/resource/a71ec9ee838e17ff2e49dbcc7451790f/xmlnote/WEBRESOURCE3a8233aa379e3bc6a52b742963e70b0f/5700" alt="node.js 事件循环示意图"></p>
<p>根据上图，Node.js的运行机制如下。</p>
<ol>
<li>V8 引擎解析 JavaScript 脚本。</li>
<li>解析后的代码，调用 Node API。</li>
<li>libuv 库负责 Node API 的执行。它将不同的任务分配给不同的线程，形成一个 Event Loop（事件循环），以异步的方式将任务的执行结果返回给V8引擎。</li>
<li>V8 引擎再将结果返回给用户。</li>
</ol>
<p>除了 <code>setTimeout</code> 和 <code>setInterval</code> 这两个方法，Node.js 还提供了另外两个与”任务队列”有关的方法：<code>process.nextTick</code> 和 <code>setImmediate</code>。它们可以帮助我们加深对”任务队列”的理解。</p>
<p><code>process.nextTick</code> 方法可以在当前”执行栈”的尾部—-下一次 Event Loop（主线程读取”任务队列”）之前—-触发回调函数。也就是说，它指定的任务总是发生在所有异步任务之前。<code>setImmediate</code> 方法则是在当前”任务队列”的尾部添加事件，也就是说，它指定的任务总是在下一次 Event Loop 时执行，这与 <code>setTimeout(fn, 0)</code> 很像。请看下面的例子（via StackOverflow）。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">process.nextTick(function A() &#123;</span><br><span class="line">  console.log(1);</span><br><span class="line">  process.nextTick(function B()&#123;console.log(2);&#125;);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">setTimeout(function timeout() &#123;</span><br><span class="line">  console.log(&#39;TIMEOUT FIRED&#39;);</span><br><span class="line">&#125;, 0)</span><br><span class="line">&#x2F;&#x2F; 1</span><br><span class="line">&#x2F;&#x2F; 2</span><br><span class="line">&#x2F;&#x2F; TIMEOUT FIRED</span><br></pre></td></tr></table></figure>

<p>上面代码中，由于 <code>process.nextTick</code> 方法指定的回调函数，总是在当前”执行栈”的尾部触发，所以不仅函数 <code>A</code> 比 <code>setTimeout</code> 指定的回调函数 <code>timeout</code> 先执行，而且函数 <code>B</code> 也比 <code>timeout</code> 先执行。这说明，如果有多个 <code>process.nextTick</code> 语句（不管它们是否嵌套），将全部在当前”执行栈”执行。</p>
<p>现在，再看 <code>setImmediate</code>。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">setImmediate(function A() &#123;</span><br><span class="line">  console.log(1);</span><br><span class="line">  setImmediate(function B()&#123;console.log(2);&#125;);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">setTimeout(function timeout() &#123;</span><br><span class="line">  console.log(&#39;TIMEOUT FIRED&#39;);</span><br><span class="line">&#125;, 0);</span><br></pre></td></tr></table></figure>

<p>上面代码中，<code>setImmediate</code> 与 <code>setTimeout(fn,0)</code> 各自添加了一个回调函数 <code>A</code> 和 <code>timeout</code>，都是在下一次 Event Loop 触发。那么，哪个回调函数先执行呢？答案是不确定。运行结果可能是 <code>1--TIMEOUT FIRED--2</code>，也可能是 <code>TIMEOUT FIRED--1--2</code>。</p>
<p>令人困惑的是，<code>Node.js</code> 文档中称，<code>setImmediate</code> 指定的回调函数，总是排在 <code>setTimeout</code> 前面。实际上，这种情况只发生在递归调用的时候。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">setImmediate(function ()&#123;</span><br><span class="line">  setImmediate(function A() &#123;</span><br><span class="line">    console.log(1);</span><br><span class="line">    setImmediate(function B()&#123;console.log(2);&#125;);</span><br><span class="line">  &#125;);</span><br><span class="line"></span><br><span class="line">  setTimeout(function timeout() &#123;</span><br><span class="line">    console.log(&#39;TIMEOUT FIRED&#39;);</span><br><span class="line">  &#125;, 0);</span><br><span class="line">&#125;);</span><br><span class="line">&#x2F;&#x2F; 1</span><br><span class="line">&#x2F;&#x2F; TIMEOUT FIRED</span><br><span class="line">&#x2F;&#x2F; 2</span><br></pre></td></tr></table></figure>

<p>上面代码中，<code>setImmediate</code> 和 <code>setTimeout</code> 被封装在一个 <code>setImmediate</code> 里面，它的运行结果总是 <code>1--TIMEOUT FIRED--2</code>，这时函数 <code>A</code> 一定在 <code>timeout</code> 前面触发。至于 <code>2</code> 排在 <code>TIMEOUT FIRED</code> 的后面（即函数 <code>B</code> 在 <code>timeout</code> 后面触发），是因为 <code>setImmediate</code> 总是将事件注册到下一轮 Event Loop，所以函数 <code>A</code> 和 <code>timeout</code> 是在同一轮 Loop 执行，而函数 <code>B</code> 在下一轮 Loop 执行。</p>
<p>我们由此得到了 <code>process.nextTick</code> 和 <code>setImmediate</code> 的一个重要区别：多个 <code>process.nextTick</code> 语句总是在当前”执行栈”一次执行完，多个 <code>setImmediate</code> 可能则需要多次 loop 才能执行完。事实上，这正是 Node.js 10.0 版添加 <code>setImmediate</code> 方法的原因，否则像下面这样的递归调用 <code>process.nextTick</code>，将会没完没了，主线程根本不会去读取”事件队列”！</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">process.nextTick(function foo() &#123;</span><br><span class="line">  process.nextTick(foo);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<p>事实上，现在要是你写出递归的 <code>process.nextTick</code>，Node.js 会抛出一个警告，要求你改成 <code>setImmediate</code>。</p>
<p>另外，由于 <code>process.nextTick</code> 指定的回调函数是在本次”事件循环”触发，而 <code>setImmediate</code> 指定的是在下次”事件循环”触发，所以很显然，前者总是比后者发生得早，而且执行效率也高（因为不用检查”任务队列”）。</p>

      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://yoursite.com/2017/05/21/%E5%AF%B9%E8%B1%A1%E7%9A%84%E7%BB%A7%E6%89%BF/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="https://inews.gtimg.com/newsapp_ls/0/12122532695/0">
      <meta itemprop="name" content="Albert Guo">
      <meta itemprop="description" content="郭鹏松的个人博客">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Albert Guo">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/2017/05/21/%E5%AF%B9%E8%B1%A1%E7%9A%84%E7%BB%A7%E6%89%BF/" class="post-title-link" itemprop="url">对象的继承</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2017-05-21 13:50:51" itemprop="dateCreated datePublished" datetime="2017-05-21T13:50:51+08:00">2017-05-21</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2020-07-22 19:58:50" itemprop="dateModified" datetime="2020-07-22T19:58:50+08:00">2020-07-22</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/JavaScript/" itemprop="url" rel="index"><span itemprop="name">JavaScript</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <blockquote>
<p>本文部分摘录自网道 JavaScript 教程<br>构造函数非构造函数部分搬运自 <em>阮一峰</em> 博客<br><a href="http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_inheritance.html" target="_blank" rel="noopener">http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_inheritance.html</a><br><a href="https://wangdoc.com/javascript/oop/prototype.html#prototype-%E5%B1%9E%E6%80%A7%E7%9A%84%E4%BD%9C%E7%94%A8" target="_blank" rel="noopener">https://wangdoc.com/javascript/oop/prototype.html#prototype-%E5%B1%9E%E6%80%A7%E7%9A%84%E4%BD%9C%E7%94%A8</a>    </p>
</blockquote>
<p>面向对象编程很重要的一个方面，就是对象的继承。A 对象通过继承 B 对象，就能直接拥有 B 对象的所有属性和方法。这对于代码的复用是非常有用的。</p>
<p>大部分面向对象的编程语言，都是通过“类”（class）实现对象的继承。传统上，JavaScript 语言的继承不通过 class，而是通过“原型对象”（prototype）实现。</p>
<h1 id="原型对象概述"><a href="#原型对象概述" class="headerlink" title="原型对象概述"></a>原型对象概述</h1><h2 id="构造函数的缺点"><a href="#构造函数的缺点" class="headerlink" title="构造函数的缺点"></a>构造函数的缺点</h2><p>通过构造函数为实例对象定义属性，虽然方便，但是又个缺点，==同一个构造函数的多个实例对象之间，无法共享属性，从而对系统资源造成浪费。==</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Cat</span>(<span class="params">name,color</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.name = name;</span><br><span class="line">    <span class="keyword">this</span>.color = color;</span><br><span class="line">    <span class="keyword">this</span>.meow = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">'喵喵'</span>)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> cat1 = <span class="keyword">new</span> Cat(<span class="string">'大毛'</span>,<span class="string">'白色'</span>)；</span><br><span class="line"><span class="keyword">var</span> cat2 = <span class="keyword">new</span> Cat(<span class="string">'二毛'</span>,<span class="string">'黄色'</span>);</span><br><span class="line"></span><br><span class="line">cat1.meow === cat2.meow <span class="comment">// false</span></span><br></pre></td></tr></table></figure>

<h2 id="prototype-属性的作用"><a href="#prototype-属性的作用" class="headerlink" title="prototype 属性的作用"></a>prototype 属性的作用</h2><p>JavaScript 继承机制的设计思想：==原型对象的所有属性和方法，都能被实例对象共享。如果属性和方法定义在原型上，那么所有实例就能共享。==</p>
<p>==JavaScript 规定，每个函数都有一个 <code>prototype</code> 属性，指向一个对象。==</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">f</span>(<span class="params"></span>) </span>&#123; &#125;</span><br><span class="line"><span class="keyword">typeof</span> f.prototype <span class="comment">// 'object'</span></span><br></pre></td></tr></table></figure>

<p>对于普通函数来说，该属性基本无用。==对于构造函数来说，生成实例的时候，该属性（<code>prototype</code>）会自动成为实例对象的原型。==</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Animal</span>(<span class="params">name</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">this</span>.name = name;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Animal.prototype.color = <span class="string">'white'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> cat1 = <span class="keyword">new</span> Animal(<span class="string">'大毛'</span>);</span><br><span class="line"><span class="keyword">var</span> cat2 = <span class="keyword">new</span> Animal(<span class="string">'二毛'</span>);</span><br><span class="line"></span><br><span class="line">cat1.color <span class="comment">// 'white'</span></span><br><span class="line">cat2.color <span class="comment">// 'white'</span></span><br></pre></td></tr></table></figure>

<p>==原型对象的属性不是实例对象自身的属性。只要修改原型对象，变动就会立刻体现在所有实例对象上。==</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Animal.prototype.color = <span class="string">'yellow'</span>;</span><br><span class="line"></span><br><span class="line">cat1.color <span class="comment">// "yellow"</span></span><br><span class="line">cat2.color <span class="comment">// "yellow"</span></span><br></pre></td></tr></table></figure>

<p>==如果实例对象自身就有某个属性或者方法，它就不会再去原型对象寻找这个属性或者方法。==</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">cat1.color = <span class="string">'black'</span>;</span><br><span class="line"></span><br><span class="line">cat1.color <span class="comment">// 'black'</span></span><br><span class="line">cat2.color <span class="comment">// 'yellow'</span></span><br><span class="line">Animal.prototype.color <span class="comment">// 'yellow';</span></span><br></pre></td></tr></table></figure>

<p>==原型对象的作用，就是定义所有实例对象共享的属性和方法。==</p>
<h2 id="原型链"><a href="#原型链" class="headerlink" title="原型链"></a>原型链</h2><p>==JavaScript 规定，所有的对象都有自己的原型对象（<code>prototype</code>）。一方面，任何一个对象，都可以充当其它对象的原型；另一方面，由于原型对象也是对象，所以它也有自己的原型。因此，就会形成一个“原型链”：对象到原型，再到原型的原型……==</p>
<p>==一层一层往上追溯，所有的对象原型最终都可以追溯到 <code>Object.prototype</code> ，即 <code>Object</code> 构造函数的 <code>prototype</code> 属性。<code>Object.prototype</code> 的原型是 <code>null</code>。<code>null</code> 没有任何属性和方法，也没有自己的原型。因此，原型链的尽头就是 <code>null</code>。==</p>
<h2 id="constructor-属性"><a href="#constructor-属性" class="headerlink" title="constructor 属性"></a>constructor 属性</h2><p>==<code>prototype</code> 对象有一个 <code>constructor</code> 属性，默认指向 <code>prototype</code> 对象所在的构造函数。==</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">P</span>(<span class="params"></span>)</span>&#123; &#125;;</span><br><span class="line"></span><br><span class="line">P.prototype.constructor === P <span class="comment">// ture</span></span><br></pre></td></tr></table></figure>

<p>==<code>constructor</code> 属性表示原型对象与构造函数之间的关联关系，如果修改了原型对象，一般会同时修改 <code>constructor</code> 属性，防止引用的时候出错。==</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 坏的写法</span></span><br><span class="line">C.prototype = &#123;</span><br><span class="line">    method1:<span class="function"><span class="keyword">function</span>(<span class="params">...</span>)</span>&#123;...&#125;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 好的写法</span></span><br><span class="line">C.prototype = &#123;</span><br><span class="line">    <span class="keyword">constructor</span>:C,</span><br><span class="line">    method1:function(...)&#123;...&#125;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 更好的写法</span></span><br><span class="line">C.prototype.method1 = <span class="function"><span class="keyword">function</span>(<span class="params">...</span>)</span>&#123;...&#125;</span><br></pre></td></tr></table></figure>

<p>确定 <code>constructor</code> 属性是什么函数，可以使用 <code>name</code> 属性，从实例得到构造函数的名称。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Foo</span> (<span class="params"></span>) </span>&#123; &#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> f = <span class="keyword">new</span> Foo()</span><br><span class="line"></span><br><span class="line">f.constructor.name <span class="comment">// "Foo"</span></span><br></pre></td></tr></table></figure>

<h1 id="instanceof-运算符"><a href="#instanceof-运算符" class="headerlink" title="instanceof 运算符"></a>instanceof 运算符</h1><p>==<code>instanceof</code> 运算符返回一个布尔值，表示对象是否是某个构造函数的实例。==</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> p = <span class="keyword">new</span> Person();</span><br><span class="line"></span><br><span class="line">p <span class="keyword">instanceof</span> Person <span class="comment">// true</span></span><br></pre></td></tr></table></figure>

<p>==由于 <code>instanceof</code> 检查整个原型链，因此同一个实例对象，可能会对多个构造函数都返回 <code>true</code>。==</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> d = <span class="keyword">new</span> <span class="built_in">Date</span>();</span><br><span class="line"></span><br><span class="line">d <span class="keyword">instanceof</span> <span class="built_in">Date</span> <span class="comment">// true</span></span><br><span class="line">d <span class="keyword">instanceof</span> <span class="built_in">Object</span> <span class="comment">// true</span></span><br></pre></td></tr></table></figure>

<p>==<code>instanceof</code> 运算符只能适用于对象，不适用于原始类型的值。==</p>
<h1 id="构造函数继承"><a href="#构造函数继承" class="headerlink" title="构造函数继承"></a>构造函数继承</h1><h2 id="思考"><a href="#思考" class="headerlink" title="思考"></a>思考</h2><p>现在有一个”动物”对象的构造函数。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Animal</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">　<span class="keyword">this</span>.species = <span class="string">"动物"</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>还有一个”猫”对象的构造函数。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Cat</span>(<span class="params">name,color</span>)</span>&#123;</span><br><span class="line">　　<span class="keyword">this</span>.name = name;</span><br><span class="line">　　<span class="keyword">this</span>.color = color;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>怎样才能使”猫”继承”动物”呢？</p>
<h2 id="一、构造函数绑定"><a href="#一、构造函数绑定" class="headerlink" title="一、构造函数绑定"></a>一、构造函数绑定</h2><p>第一种方法也是最简单的方法，使用call或apply方法，将父对象的构造函数绑定在子对象上，即在子对象构造函数中加一行：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">　　<span class="function"><span class="keyword">function</span> <span class="title">Cat</span>(<span class="params">name,color</span>)</span>&#123;</span><br><span class="line"></span><br><span class="line">　　　　Animal.apply(<span class="keyword">this</span>, <span class="built_in">arguments</span>);</span><br><span class="line"></span><br><span class="line">　　　　<span class="keyword">this</span>.name = name;</span><br><span class="line"></span><br><span class="line">　　　　<span class="keyword">this</span>.color = color;</span><br><span class="line"></span><br><span class="line">　　&#125;</span><br><span class="line"></span><br><span class="line">　　<span class="keyword">var</span> cat1 = <span class="keyword">new</span> Cat(<span class="string">"大毛"</span>,<span class="string">"黄色"</span>);</span><br><span class="line"></span><br><span class="line">　　alert(cat1.species); <span class="comment">// 动物</span></span><br></pre></td></tr></table></figure>

<h2 id="二、prototype-模式"><a href="#二、prototype-模式" class="headerlink" title="二、prototype 模式"></a>二、prototype 模式</h2><p>第二种方法更常见，使用 <code>prototype</code> 属性。</p>
<p>如果”猫”的 <code>prototype</code> 对象，指向一个 <code>Animal</code> 的实例，那么所有”猫”的实例，就能继承 <code>Animal</code> 了。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">　　Cat.prototype = <span class="keyword">new</span> Animal();</span><br><span class="line"></span><br><span class="line">　　Cat.prototype.constructor = Cat;</span><br><span class="line"></span><br><span class="line">　　<span class="keyword">var</span> cat1 = <span class="keyword">new</span> Cat(<span class="string">"大毛"</span>,<span class="string">"黄色"</span>);</span><br><span class="line"></span><br><span class="line">　　alert(cat1.species); <span class="comment">// 动物</span></span><br></pre></td></tr></table></figure>

<p>代码的第一行，我们将 <code>Cat</code> 的 <code>prototype</code> 对象指向一个 <code>Animal</code> 的实例。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">　　Cat.prototype = <span class="keyword">new</span> Animal();</span><br></pre></td></tr></table></figure>

<p>它相当于完全删除了 <code>prototype</code> 对象原先的值，然后赋予一个新值。但是，第二行又是什么意思呢？</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">　　Cat.prototype.constructor = Cat;</span><br></pre></td></tr></table></figure>

<p>原来，任何一个 <code>prototype</code> 对象都有一个 <code>constructor</code> 属性，指向它的构造函数。如果没有 <code>&quot;Cat.prototype = new Animal();&quot;</code> 这一行，<code>Cat.prototype.constructor</code> 是指向 <code>Cat</code> 的；加了这一行以后，<code>Cat.prototype.constructor</code> 指向 <code>Animal</code>。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">　　alert(Cat.prototype.constructor == Animal); <span class="comment">//true</span></span><br></pre></td></tr></table></figure>

<p>更重要的是，每一个实例也有一个 <code>constructor</code> 属性，默认调用 <code>prototype</code> 对象的 <code>constructor</code> 属性。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">　　alert(cat1.constructor == Cat.prototype.constructor); <span class="comment">// true</span></span><br></pre></td></tr></table></figure>

<p>因此，在运行 <code>&quot;Cat.prototype = new Animal();&quot;</code> 这一行之后，<code>cat1.constructor</code> 也指向 <code>Animal</code> ！</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">　　alert(cat1.constructor == Animal); <span class="comment">// true</span></span><br></pre></td></tr></table></figure>

<p>这显然会导致继承链的紊乱（<code>cat1</code> 明明是用构造函数 <code>Cat</code> 生成的），因此我们必须手动纠正，将 <code>Cat.prototype</code> 对象的 <code>constructor</code> 值改为 <code>Cat</code>。这就是第二行的意思。</p>
<p>这是很重要的一点，编程时务必要遵守。下文都遵循这一点，即如果替换了 <code>prototype</code> 对象，</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">　　o.prototype = &#123;&#125;;</span><br></pre></td></tr></table></figure>

<p>那么，下一步必然是为新的 <code>prototype</code> 对象加上 <code>constructor</code> 属性，并将这个属性指回原来的构造函数。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">　　o.prototype.constructor = o;</span><br></pre></td></tr></table></figure>

<h2 id="三、直接继承-prototype"><a href="#三、直接继承-prototype" class="headerlink" title="三、直接继承 prototype"></a>三、直接继承 prototype</h2><p>第三种方法是对第二种方法的改进。由于 <code>Animal</code> 对象中，不变的属性都可以直接写入 <code>Animal.prototype</code>。所以，我们也可以让 <code>Cat()</code> 跳过 <code>Animal()</code>，直接继承 <code>Animal.prototype</code>。</p>
<p>现在，我们先将 <code>Animal</code> 对象改写：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">　　<span class="function"><span class="keyword">function</span> <span class="title">Animal</span>(<span class="params"></span>)</span>&#123; &#125;</span><br><span class="line"></span><br><span class="line">　　Animal.prototype.species = <span class="string">"动物"</span>;</span><br></pre></td></tr></table></figure>

<p>然后，将 <code>Cat</code> 的 <code>prototype</code> 对象，然后指向 <code>Animal</code> 的 <code>prototype</code> 对象，这样就完成了继承。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">　　Cat.prototype = Animal.prototype;</span><br><span class="line"></span><br><span class="line">　　Cat.prototype.constructor = Cat;</span><br><span class="line"></span><br><span class="line">　　<span class="keyword">var</span> cat1 = <span class="keyword">new</span> Cat(<span class="string">"大毛"</span>,<span class="string">"黄色"</span>);</span><br><span class="line"></span><br><span class="line">　　alert(cat1.species); <span class="comment">// 动物</span></span><br></pre></td></tr></table></figure>

<p>与前一种方法相比，这样做的优点是效率比较高（不用执行和建立 <code>Animal</code> 的实例了），比较省内存。缺点是  <code>Cat.prototype</code> 和 <code>Animal.prototype</code> 现在指向了同一个对象，那么任何对 <code>Cat.prototype</code> 的修改，都会反映到 <code>Animal.prototype</code>。</p>
<p>所以，上面这一段代码其实是有问题的。请看第二行</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">　　Cat.prototype.constructor = Cat;</span><br></pre></td></tr></table></figure>

<p>这一句实际上把 <code>Animal.prototype</code> 对象的 <code>constructor</code> 属性也改掉了！</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">　　alert(Animal.prototype.constructor); <span class="comment">// Cat</span></span><br></pre></td></tr></table></figure>

<h2 id="四、利用空对象作为中介"><a href="#四、利用空对象作为中介" class="headerlink" title="四、利用空对象作为中介"></a>四、利用空对象作为中介</h2><p>由于”直接继承 <code>prototype</code>“存在上述的缺点，所以就有第四种方法，利用一个空对象作为中介。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">　　<span class="keyword">var</span> F = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;&#125;;</span><br><span class="line"></span><br><span class="line">　　F.prototype = Animal.prototype;</span><br><span class="line"></span><br><span class="line">　　Cat.prototype = <span class="keyword">new</span> F();</span><br><span class="line"></span><br><span class="line">　　Cat.prototype.constructor = Cat;</span><br></pre></td></tr></table></figure>

<p><code>F</code> 是空对象，所以几乎不占内存。这时，修改 <code>Cat</code> 的 <code>prototype</code> 对象，就不会影响到 <code>Animal</code> 的 <code>prototype</code> 对象。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">　　alert(Animal.prototype.constructor); <span class="comment">// Animal</span></span><br></pre></td></tr></table></figure>

<p>我们将上面的方法，封装成一个函数，便于使用。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">　　<span class="function"><span class="keyword">function</span> <span class="title">extend</span>(<span class="params">Child, Parent</span>) </span>&#123;</span><br><span class="line"></span><br><span class="line">　　　　<span class="keyword">var</span> F = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;&#125;;</span><br><span class="line"></span><br><span class="line">　　　　F.prototype = Parent.prototype;</span><br><span class="line"></span><br><span class="line">　　　　Child.prototype = <span class="keyword">new</span> F();</span><br><span class="line"></span><br><span class="line">　　　　Child.prototype.constructor = Child;</span><br><span class="line"></span><br><span class="line">　　　　Child.uber = Parent.prototype;</span><br><span class="line"></span><br><span class="line">　　&#125;</span><br></pre></td></tr></table></figure>

<p>使用的时候，方法如下</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">　　extend(Cat,Animal);</span><br><span class="line"></span><br><span class="line">　　<span class="keyword">var</span> cat1 = <span class="keyword">new</span> Cat(<span class="string">"大毛"</span>,<span class="string">"黄色"</span>);</span><br><span class="line"></span><br><span class="line">　　alert(cat1.species); <span class="comment">// 动物</span></span><br></pre></td></tr></table></figure>

<p>这个 <code>extend</code> 函数，就是 <code>YUI</code> 库如何实现继承的方法。</p>
<p>另外，说明一点，函数体最后一行</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">　　Child.uber = Parent.prototype;</span><br></pre></td></tr></table></figure>

<p>意思是为子对象设一个 <code>uber</code> 属性，这个属性直接指向父对象的 <code>prototype</code> 属性。（<code>uber</code> 是一个德语词，意思是”向上”、”上一层”。）这等于在子对象上打开一条通道，可以直接调用父对象的方法。这一行放在这里，只是为了实现继承的完备性，纯属备用性质。</p>
<h2 id="五、拷贝继承"><a href="#五、拷贝继承" class="headerlink" title="五、拷贝继承"></a>五、拷贝继承</h2><p>上面是采用 <code>prototype</code> 对象，实现继承。我们也可以换一种思路，纯粹采用”拷贝”方法实现继承。简单说，如果把父对象的所有属性和方法，拷贝进子对象，不也能够实现继承吗？这样我们就有了第五种方法。</p>
<p>首先，还是把 <code>Animal</code> 的所有不变属性，都放到它的 <code>prototype</code> 对象上。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">　　<span class="function"><span class="keyword">function</span> <span class="title">Animal</span>(<span class="params"></span>)</span>&#123;&#125;</span><br><span class="line"></span><br><span class="line">　　Animal.prototype.species = <span class="string">"动物"</span>;</span><br></pre></td></tr></table></figure>

<p>然后，再写一个函数，实现属性拷贝的目的。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">　　<span class="function"><span class="keyword">function</span> <span class="title">extend2</span>(<span class="params">Child, Parent</span>) </span>&#123;</span><br><span class="line"></span><br><span class="line">　　　　<span class="keyword">var</span> p = Parent.prototype;</span><br><span class="line"></span><br><span class="line">　　　　<span class="keyword">var</span> c = Child.prototype;</span><br><span class="line"></span><br><span class="line">　　　　<span class="keyword">for</span> (<span class="keyword">var</span> i <span class="keyword">in</span> p) &#123;</span><br><span class="line"></span><br><span class="line">　　　　　　c[i] = p[i];</span><br><span class="line"></span><br><span class="line">　　　　　　&#125;</span><br><span class="line"></span><br><span class="line">　　　　c.uber = p;</span><br><span class="line"></span><br><span class="line">　　&#125;</span><br></pre></td></tr></table></figure>

<p>这个函数的作用，就是将父对象的 <code>prototype</code> 对象中的属性，一一拷贝给 <code>Child</code> 对象的 <code>prototype</code> 对象。</p>
<p>使用的时候，这样写：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">　　extend2(Cat, Animal);</span><br><span class="line"></span><br><span class="line">　　<span class="keyword">var</span> cat1 = <span class="keyword">new</span> Cat(<span class="string">"大毛"</span>,<span class="string">"黄色"</span>);</span><br><span class="line"></span><br><span class="line">　　alert(cat1.species); <span class="comment">// 动物</span></span><br></pre></td></tr></table></figure>

<h1 id="非构造函数继承"><a href="#非构造函数继承" class="headerlink" title="非构造函数继承"></a>非构造函数继承</h1><h2 id="思考-1"><a href="#思考-1" class="headerlink" title="思考"></a>思考</h2><p>什么是”非构造函数”的继承？</p>
<p>比如，现在有一个对象，叫做”中国人”。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">　　<span class="keyword">var</span> Chinese = &#123;</span><br><span class="line">　　　　nation:<span class="string">'中国'</span></span><br><span class="line">　　&#125;;</span><br></pre></td></tr></table></figure>

<p>还有一个对象，叫做”医生”。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">　　<span class="keyword">var</span> Doctor =&#123;</span><br><span class="line">　　　　career:<span class="string">'医生'</span></span><br><span class="line">　　&#125;</span><br></pre></td></tr></table></figure>

<p>请问怎样才能让”医生”去继承”中国人”，也就是说，我怎样才能生成一个”中国医生”的对象？</p>
<p>这里要注意，这两个对象都是普通对象，不是构造函数，无法使用构造函数方法实现”继承”。</p>
<h2 id="一、object-方法"><a href="#一、object-方法" class="headerlink" title="一、object() 方法"></a>一、object() 方法</h2><p><code>json</code> 格式的发明人 Douglas Crockford，提出了一个 <code>object()</code> 函数，可以做到这一点。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">　　<span class="function"><span class="keyword">function</span> <span class="title">object</span>(<span class="params">o</span>) </span>&#123;</span><br><span class="line"></span><br><span class="line">　　　　<span class="function"><span class="keyword">function</span> <span class="title">F</span>(<span class="params"></span>) </span>&#123;&#125;</span><br><span class="line"></span><br><span class="line">　　　　F.prototype = o;</span><br><span class="line"></span><br><span class="line">　　　　<span class="keyword">return</span> <span class="keyword">new</span> F();</span><br><span class="line"></span><br><span class="line">　　&#125;</span><br></pre></td></tr></table></figure>

<p>这个 <code>object()</code> 函数，其实只做一件事，就是把子对象的 <code>prototype</code> 属性，指向父对象，从而使得子对象与父对象连在一起。</p>
<p>使用的时候，第一步先在父对象的基础上，生成子对象：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">　　<span class="keyword">var</span> Doctor = object(Chinese);</span><br></pre></td></tr></table></figure>

<p>然后，再加上子对象本身的属性：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">　　Doctor.career = <span class="string">'医生'</span>;</span><br></pre></td></tr></table></figure>

<p>这时，子对象已经继承了父对象的属性了。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">　　alert(Doctor.nation); <span class="comment">//中国</span></span><br></pre></td></tr></table></figure>

<h2 id="二、浅拷贝"><a href="#二、浅拷贝" class="headerlink" title="二、浅拷贝"></a>二、浅拷贝</h2><p>除了使用 <code>&quot;prototype链&quot;</code> 以外，还有另一种思路：把父对象的属性，全部拷贝给子对象，也能实现继承。</p>
<p>下面这个函数，就是在做拷贝：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">　　<span class="function"><span class="keyword">function</span> <span class="title">extendCopy</span>(<span class="params">p</span>) </span>&#123;</span><br><span class="line"></span><br><span class="line">　　　　<span class="keyword">var</span> c = &#123;&#125;;</span><br><span class="line"></span><br><span class="line">　　　　<span class="keyword">for</span> (<span class="keyword">var</span> i <span class="keyword">in</span> p) &#123; </span><br><span class="line">　　　　　　c[i] = p[i];</span><br><span class="line">　　　　&#125;</span><br><span class="line"></span><br><span class="line">　　　　c.uber = p;</span><br><span class="line"></span><br><span class="line">　　　　<span class="keyword">return</span> c;</span><br><span class="line">　　&#125;</span><br></pre></td></tr></table></figure>

<p>使用的时候，这样写：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">　　<span class="keyword">var</span> Doctor = extendCopy(Chinese);</span><br><span class="line"></span><br><span class="line">　　Doctor.career = <span class="string">'医生'</span>;</span><br><span class="line"></span><br><span class="line">　　alert(Doctor.nation); <span class="comment">// 中国</span></span><br></pre></td></tr></table></figure>

<p>但是，这样的拷贝有一个问题。那就是，如果父对象的属性等于数组或另一个对象，那么实际上，子对象获得的只是一个内存地址，而不是真正拷贝，因此存在父对象被篡改的可能。</p>
<p>请看，现在给 <code>Chinese</code> 添加一个”出生地”属性，它的值是一个数组。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">　　Chinese.birthPlaces = [<span class="string">'北京'</span>,<span class="string">'上海'</span>,<span class="string">'香港'</span>];</span><br></pre></td></tr></table></figure>

<p>通过 <code>extendCopy()</code> 函数，<code>Doctor</code> 继承了 <code>Chinese</code>。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">　　<span class="keyword">var</span> Doctor = extendCopy(Chinese);</span><br></pre></td></tr></table></figure>

<p>然后，我们为 <code>Doctor</code> 的”出生地”添加一个城市：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">　　Doctor.birthPlaces.push(<span class="string">'厦门'</span>);</span><br></pre></td></tr></table></figure>

<p>发生了什么事？<code>Chinese</code> 的”出生地”也被改掉了！</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">　　alert(Doctor.birthPlaces); <span class="comment">//北京, 上海, 香港, 厦门</span></span><br><span class="line"></span><br><span class="line">　　alert(Chinese.birthPlaces); <span class="comment">//北京, 上海, 香港, 厦门</span></span><br></pre></td></tr></table></figure>

<p>所以，<code>extendCopy()</code> 只是拷贝基本类型的数据，我们把这种拷贝叫做”浅拷贝”。这是早期 <code>jQuery</code> 实现继承的方式。</p>
<h2 id="三、深拷贝"><a href="#三、深拷贝" class="headerlink" title="三、深拷贝"></a>三、深拷贝</h2><p>所谓”深拷贝”，就是能够实现真正意义上的数组和对象的拷贝。它的实现并不难，只要递归调用”浅拷贝”就行了。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">　　<span class="function"><span class="keyword">function</span> <span class="title">deepCopy</span>(<span class="params">p, c</span>) </span>&#123;</span><br><span class="line"></span><br><span class="line">　　　　<span class="keyword">var</span> c = c || &#123;&#125;;</span><br><span class="line"></span><br><span class="line">　　　　<span class="keyword">for</span> (<span class="keyword">var</span> i <span class="keyword">in</span> p) &#123;</span><br><span class="line"></span><br><span class="line">　　　　　　<span class="keyword">if</span> (<span class="keyword">typeof</span> p[i] === <span class="string">'object'</span>) &#123;</span><br><span class="line"></span><br><span class="line">　　　　　　　　c[i] = (p[i].constructor === <span class="built_in">Array</span>) ? [] : &#123;&#125;;</span><br><span class="line"></span><br><span class="line">　　　　　　　　deepCopy(p[i], c[i]);</span><br><span class="line"></span><br><span class="line">　　　　　　&#125; <span class="keyword">else</span> &#123;</span><br><span class="line"></span><br><span class="line">　　　　　　　　　c[i] = p[i];</span><br><span class="line"></span><br><span class="line">　　　　　　&#125;</span><br><span class="line">　　　　&#125;</span><br><span class="line"></span><br><span class="line">　　　　<span class="keyword">return</span> c;</span><br><span class="line">　　&#125;</span><br></pre></td></tr></table></figure>

<p>使用的时候这样写：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">　　<span class="keyword">var</span> Doctor = deepCopy(Chinese);</span><br></pre></td></tr></table></figure>

<p>现在，给父对象加一个属性，值为数组。然后，在子对象上修改这个属性：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">　　Chinese.birthPlaces = [<span class="string">'北京'</span>,<span class="string">'上海'</span>,<span class="string">'香港'</span>];</span><br><span class="line"></span><br><span class="line">　　Doctor.birthPlaces.push(<span class="string">'厦门'</span>);</span><br></pre></td></tr></table></figure>

<p>这时，父对象就不会受到影响了。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">　　alert(Doctor.birthPlaces); <span class="comment">//北京, 上海, 香港, 厦门</span></span><br><span class="line"></span><br><span class="line">　　alert(Chinese.birthPlaces); <span class="comment">//北京, 上海, 香港</span></span><br></pre></td></tr></table></figure>

<p>目前，<code>jQuery</code> 库使用的就是这种继承方法。</p>
<h1 id="多重继承"><a href="#多重继承" class="headerlink" title="多重继承"></a>多重继承</h1><p>JavaScript 不提供多重继承功能，即不允许一个对象同时继承多个对象。但是，可以通过变通方法，实现这个功能。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">M1</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">this</span>.hello = <span class="string">'hello'</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">M2</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">this</span>.world = <span class="string">'world'</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">S</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  M1.call(<span class="keyword">this</span>);</span><br><span class="line">  M2.call(<span class="keyword">this</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 继承 M1</span></span><br><span class="line">S.prototype = <span class="built_in">Object</span>.create(M1.prototype);</span><br><span class="line"><span class="comment">// 继承链上加入 M2</span></span><br><span class="line"><span class="built_in">Object</span>.assign(S.prototype, M2.prototype);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 指定构造函数</span></span><br><span class="line">S.prototype.constructor = S;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> s = <span class="keyword">new</span> S();</span><br><span class="line">s.hello <span class="comment">// 'hello'</span></span><br><span class="line">s.world <span class="comment">// 'world'</span></span><br></pre></td></tr></table></figure>

<p>上面代码中，子类S同时继承了父类 M1 和 M2 。这种模式又称为 <code>Mixin</code>（混入）。</p>

      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://yoursite.com/2017/03/23/%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0%E5%A6%82%E4%BD%95%E5%88%9B%E5%BB%BA%E5%AF%B9%E8%B1%A1%EF%BC%9F/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="https://inews.gtimg.com/newsapp_ls/0/12122532695/0">
      <meta itemprop="name" content="Albert Guo">
      <meta itemprop="description" content="郭鹏松的个人博客">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Albert Guo">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/2017/03/23/%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0%E5%A6%82%E4%BD%95%E5%88%9B%E5%BB%BA%E5%AF%B9%E8%B1%A1%EF%BC%9F/" class="post-title-link" itemprop="url">构造函数如何创建对象？</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2017-03-23 20:02:00" itemprop="dateCreated datePublished" datetime="2017-03-23T20:02:00+08:00">2017-03-23</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2020-07-23 20:04:04" itemprop="dateModified" datetime="2020-07-23T20:04:04+08:00">2020-07-23</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/JavaScript/" itemprop="url" rel="index"><span itemprop="name">JavaScript</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <h2 id="构造函数"><a href="#构造函数" class="headerlink" title="构造函数"></a>构造函数</h2><p>JavaScript 语言使用构造函数（<code>constructor</code>）作为对象的模板。所谓“构造函数”，就是专门用来生成实例对象的函数。</p>
<p>构造函数就是一个普通的函数，为了与普通函数区别，构造函数名字的第一个字母通常大写。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> Vehicle = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">this</span>.price = <span class="number">1000</span>;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>构造函数特点：</p>
<ul>
<li>函数体内部使用了 <code>this</code> 关键字，代表了所要生成对象的实例。</li>
<li>生成对象时，必须使用 <code>new</code> 命令。</li>
</ul>
<h2 id="new-命令"><a href="#new-命令" class="headerlink" title="new 命令"></a>new 命令</h2><p><code>new</code> 命令的作用，就是执行构造函数，返回一个实例对象。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> Vehicle = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">this</span>.price = <span class="number">1000</span>;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> v = <span class="keyword">new</span> Vehicle();</span><br><span class="line">v.price <span class="comment">// 1000</span></span><br></pre></td></tr></table></figure>

<p><code>new</code> 命令原理：</p>
<ul>
<li>创建一个空对象，作为要返回的对象实例。</li>
<li>将这个空对象的原型，指向构造函数的 <code>prototype</code> 属性。</li>
<li>将这个空对象赋值给函数内部的 <code>this</code> 关键字。</li>
<li>开始执行构造函数内部代码。</li>
</ul>

      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://yoursite.com/2016/10/12/%E5%89%8D%E7%AB%AF%E6%A8%A1%E5%9D%97%E5%8C%96%E5%AF%B9%E6%AF%94%E6%80%BB%E7%BB%93/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="https://inews.gtimg.com/newsapp_ls/0/12122532695/0">
      <meta itemprop="name" content="Albert Guo">
      <meta itemprop="description" content="郭鹏松的个人博客">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Albert Guo">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/2016/10/12/%E5%89%8D%E7%AB%AF%E6%A8%A1%E5%9D%97%E5%8C%96%E5%AF%B9%E6%AF%94%E6%80%BB%E7%BB%93/" class="post-title-link" itemprop="url">前端模块化对比总结</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2016-10-12 16:31:38" itemprop="dateCreated datePublished" datetime="2016-10-12T16:31:38+08:00">2016-10-12</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2020-11-26 20:46:15" itemprop="dateModified" datetime="2020-11-26T20:46:15+08:00">2020-11-26</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/JavaScript/" itemprop="url" rel="index"><span itemprop="name">JavaScript</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <blockquote>
<p>本文部分摘录自 <strong>阮一峰</strong> 老师博客，参考地址：<a href="https://es6.ruanyifeng.com/?search=set&amp;x=0&amp;y=0#docs/module" target="_blank" rel="noopener">https://es6.ruanyifeng.com/?search=set&amp;x=0&amp;y=0#docs/module</a><br>部分收集自网络，仅供学习参考，不可作为商业用途。</p>
</blockquote>
<h1 id="历史"><a href="#历史" class="headerlink" title="历史"></a>历史</h1><p>历史上，JavaScript 一直没有模块（module）体系，无法将一个大程序拆分成互相依赖的小文件，再用简单的方法拼装起来。</p>
<p>在 ES6 之前社区制定了一些模块加载方案，主要有 CJS、AMD、UMD，后来 ES6 诞生了 ESM 。还有一些其他模块化加载方案，但我们只讨论这几种用的范围比较广的。</p>
<hr>
<h1 id="CommonJS"><a href="#CommonJS" class="headerlink" title="CommonJS"></a>CommonJS</h1><h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><p>有的地方也叫 CJS，CJS 是 CommonJS 的缩写。是一种服务器端模块化的规范，<code>node.js</code> 的模块系统，就是典型使用 <code>CommonJS</code> 规范实现的。</p>
<p>CommonJS 分为三部分：</p>
<ul>
<li><code>exports</code> 模块导出</li>
<li><code>require</code> 模块加载</li>
<li><code>module</code> 模块本身</li>
</ul>
<p>根据规范一个单独的 JS 文件就是一个 <code>module</code>，每个单独的 <code>module</code> 是一个单独的作用域。也就是说在一个文件里定义的变量和函数都是私有，对其他文件不可见，除非用 <code>exports</code> 导出了。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//exporting</span></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="keyword">function</span> <span class="title">doSomething</span>(<span class="params">n</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// do something</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//importing </span></span><br><span class="line"><span class="keyword">const</span> doSomething = <span class="built_in">require</span>(<span class="string">'./doSomething.js'</span>);</span><br></pre></td></tr></table></figure>

<p>在 <code>CommonJS</code> 中，有一个全局性方法 <code>require()</code> ，用于加载模块。假定有一个数学模块 <code>math.js</code> ，就可以像下面这样加载。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> math = <span class="built_in">require</span>(<span class="string">'math'</span>);</span><br></pre></td></tr></table></figure>

<p>然后，就可以调用模块提供的方法：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> math = <span class="built_in">require</span>(<span class="string">'math'</span>);</span><br><span class="line">math.add(<span class="number">2</span>,<span class="number">3</span>); <span class="comment">// 5</span></span><br></pre></td></tr></table></figure>

<p>但是，由于一个重大的局限，使得 <code>CommonJS</code> 规范不适用于浏览器环境。上述代码，如果在浏览器中运行，会有一个很大的问题。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> math = <span class="built_in">require</span>(<span class="string">'math'</span>);</span><br><span class="line">math.add(<span class="number">2</span>, <span class="number">3</span>);</span><br></pre></td></tr></table></figure>

<p>第二行 <code>math.add(2, 3)</code>，在第一行 <code>require(&#39;math&#39;)</code> 之后运行，因此必须等 <code>math.js</code> 加载完成。也就是说，如果加载时间很长，整个应用就会停在那里等。</p>
<p>这对服务器端不是一个问题，因为所有的模块都存放在本地硬盘，可以同步加载完成，等待时间就是硬盘的读取时间。但是，对于浏览器，这却是一个大问题，因为模块都放在服务器端，等待时间取决于网速的快慢，可能要等很长时间，浏览器处于“假死”状态。</p>
<p>因此，浏览器端的模块，不能采用“同步加载”，只能采用“异步加载”。这就是 AMD 规范诞生的背景。</p>
<h2 id="特点"><a href="#特点" class="headerlink" title="特点"></a>特点</h2><ul>
<li>所有代码都运行在模块作用域，不会污染全局作用域</li>
<li>模块可以多次加载，但是只会在第一次加载时运行一次，然后运行结果就被缓存了，以后再加载，就直接读取缓存结果，要想让模块再次运行，必须清除缓存</li>
<li>模块加载的顺序，按照其在代码中出现的顺序</li>
</ul>
<h2 id="优点"><a href="#优点" class="headerlink" title="优点"></a>优点</h2><ul>
<li>服务器端模块便于重用</li>
<li>NPM 中已经有几十万个可以使用模块包</li>
<li>简单并容易使用</li>
</ul>
<h2 id="缺点"><a href="#缺点" class="headerlink" title="缺点"></a>缺点</h2><ul>
<li>同步的模块加载方式不适合在浏览器环境中，同步意味着阻塞加载，而浏览器资源是异步加载的</li>
<li>不能非阻塞的并行加载多个模块</li>
</ul>
<h2 id="实现方案"><a href="#实现方案" class="headerlink" title="实现方案"></a>实现方案</h2><ul>
<li>服务器端的 <code>Node.js</code></li>
<li><code>Browserify</code>(浏览器端的 CommonJS 实现，可以使用 NPM 的模块，但是编译打包后的文件体积可能很大)</li>
<li><code>modules-webmake</code>(类似 <code>Browserify</code>，还不如 <code>Browserify</code> 灵活)</li>
<li><code>wreq</code>(<code>Browserify</code> 的前身)</li>
</ul>
<hr>
<h1 id="AMD"><a href="#AMD" class="headerlink" title="AMD"></a>AMD</h1><h2 id="概述-1"><a href="#概述-1" class="headerlink" title="概述"></a>概述</h2><p>AMD（Asynchronous Module Definition）采用异步方式加载模块，模块的加载不影响它后面语句的运行。所有依赖这个模块的语句，都定义在一个回调函数中，等到加载完成之后，这个回调函数才会运行。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">define(<span class="string">"module"</span>, [<span class="string">"dep1"</span>, <span class="string">"dep2"</span>], <span class="function"><span class="keyword">function</span>(<span class="params">d1, d2</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> someExportedValue;</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="built_in">require</span>([<span class="string">"module"</span>, <span class="string">"../file"</span>], <span class="function"><span class="keyword">function</span>(<span class="params">module, file</span>) </span>&#123; <span class="comment">/* ... */</span> &#125;);</span><br></pre></td></tr></table></figure>

<h2 id="优点-1"><a href="#优点-1" class="headerlink" title="优点"></a>优点</h2><ul>
<li>适合在浏览器环境中异步加载模块</li>
<li>可以并行加载多个模块</li>
</ul>
<h2 id="缺点-1"><a href="#缺点-1" class="headerlink" title="缺点"></a>缺点</h2><ul>
<li>提高了开发成本，代码的阅读和书写比较困难，模块定义方式的语义不顺畅</li>
<li>不符合通用的模块化思维方式，是一种妥协的实现</li>
</ul>
<h2 id="实现方案-1"><a href="#实现方案-1" class="headerlink" title="实现方案"></a>实现方案</h2><ul>
<li><code>RequireJS</code></li>
<li><code>curl</code></li>
</ul>
<hr>
<h1 id="CMD"><a href="#CMD" class="headerlink" title="CMD"></a>CMD</h1><h2 id="概述-2"><a href="#概述-2" class="headerlink" title="概述"></a>概述</h2><p>CMD（Common Module Definition）规范和 AMD 很相似，尽量保持简单，并与 CommonJS 和 Node.js 的 Modules 规范保持了很大的兼容性。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">define(<span class="function"><span class="keyword">function</span>(<span class="params">require, exports, module</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">var</span> $ = <span class="built_in">require</span>(<span class="string">'jquery'</span>);</span><br><span class="line">  <span class="keyword">var</span> Spinning = <span class="built_in">require</span>(<span class="string">'./spinning'</span>);</span><br><span class="line">  exports.doSomething = ...</span><br><span class="line">  <span class="built_in">module</span>.exports = ...</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>

<h2 id="优点-2"><a href="#优点-2" class="headerlink" title="优点"></a>优点</h2><ul>
<li>依赖就近，延迟执行</li>
<li>可以很容易在 Node.js 中运行</li>
</ul>
<h2 id="缺点-2"><a href="#缺点-2" class="headerlink" title="缺点"></a>缺点</h2><ul>
<li>依赖 SPM 打包，模块的加载逻辑偏重</li>
</ul>
<h2 id="实现方案-2"><a href="#实现方案-2" class="headerlink" title="实现方案"></a>实现方案</h2><ul>
<li>Sea.js</li>
<li>coolie</li>
</ul>
<hr>
<h1 id="UMD"><a href="#UMD" class="headerlink" title="UMD"></a>UMD</h1><h2 id="概述-3"><a href="#概述-3" class="headerlink" title="概述"></a>概述</h2><p>UMD（Universal Module Definition）规范类似于兼容 CommonJS 和 AMD 的语法糖，是模块定义的跨平台解决方案。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">(<span class="function"><span class="keyword">function</span> (<span class="params">global, factory</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">typeof</span> exports === <span class="string">'object'</span> &amp;&amp; <span class="keyword">typeof</span> <span class="built_in">module</span> !== <span class="string">'undefined'</span> ? <span class="built_in">module</span>.exports = factory() :</span><br><span class="line">    <span class="keyword">typeof</span> define === <span class="string">'function'</span> &amp;&amp; define.amd ? define(factory) :</span><br><span class="line">    (global.libName = factory());</span><br><span class="line">&#125;(<span class="keyword">this</span>, (<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123; <span class="string">'use strict'</span>;&#125;)));</span><br></pre></td></tr></table></figure>

<p>可以理解 UMD 实际上就是 <code>AMD + CommonJS + 全局变量</code> 这三种风格的结合。</p>
<p>这段代码就是对当前运行环境的判断，如果是 Node 环境就是使用 CommonJs 规范， 如果不是就判断是否为 AMD 环境， 最后导出全局变量。有了 UMD 后我们的代码和同时运行在 Node 和 浏览器上。所以现在前端大多数的库最后打包都使用的是 UMD 规范。</p>
<h1 id="ESM"><a href="#ESM" class="headerlink" title="ESM"></a>ESM</h1><h2 id="概述-4"><a href="#概述-4" class="headerlink" title="概述"></a>概述</h2><p>ES6 标准增加了 JavaScript 语言层面的模块体系定义。ES6 模块的设计思想是尽量的静态化，使得编译时就能确定模块的依赖关系，以及输入和输出的变量。CommonJS 和 AMD 模块，都只能在运行时确定这些东西。比如，CommonJS 模块就是对象，输入时必须查找对象属性。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">'react'</span>;</span><br><span class="line"><span class="keyword">import</span> &#123;foo, bar&#125; <span class="keyword">from</span> <span class="string">'./myLib'</span>;</span><br><span class="line"></span><br><span class="line">...</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="comment">// your Function</span></span><br><span class="line">&#125;;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> function1() &#123;...&#125;;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> function2() &#123;...&#125;;</span><br></pre></td></tr></table></figure>

<h2 id="优点-3"><a href="#优点-3" class="headerlink" title="优点"></a>优点</h2><ul>
<li>容易进行静态分析</li>
<li>面向未来的 ECMAScript 标准</li>
</ul>
<h2 id="缺点-3"><a href="#缺点-3" class="headerlink" title="缺点"></a>缺点</h2><ul>
<li>原生浏览器端还没有实现该标准</li>
<li>全新的命令，新版的 Node.js 才支持</li>
</ul>
<h2 id="实现方案-3"><a href="#实现方案-3" class="headerlink" title="实现方案"></a>实现方案</h2><ul>
<li>Babel</li>
</ul>
<h1 id="CommonJs-和-ES6-Module-的区别"><a href="#CommonJs-和-ES6-Module-的区别" class="headerlink" title="CommonJs 和 ES6 Module 的区别"></a>CommonJs 和 ES6 Module 的区别</h1><ul>
<li>CommonJS 模块输出的是一个值的拷贝，ES6 模块输出的是值的引用</li>
<li>CommonJS 模块是运行时加载，ES6 模块是编译时输出接口</li>
</ul>

      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://yoursite.com/2016/09/15/Javascript%E5%BC%82%E6%AD%A5%E7%BC%96%E7%A8%8B%E7%9A%844%E7%A7%8D%E6%96%B9%E6%B3%95/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="https://inews.gtimg.com/newsapp_ls/0/12122532695/0">
      <meta itemprop="name" content="Albert Guo">
      <meta itemprop="description" content="郭鹏松的个人博客">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Albert Guo">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/2016/09/15/Javascript%E5%BC%82%E6%AD%A5%E7%BC%96%E7%A8%8B%E7%9A%844%E7%A7%8D%E6%96%B9%E6%B3%95/" class="post-title-link" itemprop="url">Javascript 异步编程的 4 种方法</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2016-09-15 13:50:51" itemprop="dateCreated datePublished" datetime="2016-09-15T13:50:51+08:00">2016-09-15</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2020-08-05 22:59:58" itemprop="dateModified" datetime="2020-08-05T22:59:58+08:00">2020-08-05</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/JavaScript/" itemprop="url" rel="index"><span itemprop="name">JavaScript</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <blockquote>
<p>本教程搬运自 <em>阮一峰</em> 老师博客，仅供学习参考<br>原文地址：<a href="http://www.ruanyifeng.com/blog/2012/12/asynchronous%EF%BC%BFjavascript.html" target="_blank" rel="noopener">http://www.ruanyifeng.com/blog/2012/12/asynchronous%EF%BC%BFjavascript.html</a></p>
</blockquote>
<h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><p>你可能知道，Javascript 语言的执行环境是”单线程”（single thread）。</p>
<p>所谓”单线程”，就是指一次只能完成一件任务。如果有多个任务，就必须排队，前面一个任务完成，再执行后面一个任务，以此类推。</p>
<p><img src="http://www.ruanyifeng.com/blogimg/asset/201212/bg2012122101.jpg" alt="单线程排队"></p>
<p>这种模式的好处是实现起来比较简单，执行环境相对单纯；坏处是只要有一个任务耗时很长，后面的任务都必须排队等着，会拖延整个程序的执行。常见的浏览器无响应（假死），往往就是因为某一段 Javascript 代码长时间运行（比如死循环），导致整个页面卡在这个地方，其他任务无法执行。</p>
<p>为了解决这个问题，Javascript 语言将任务的执行模式分成两种：同步（Synchronous）和异步（Asynchronous）。</p>
<h2 id="同步"><a href="#同步" class="headerlink" title="同步"></a>同步</h2><p>“同步模式” 就是上一段的模式，后一个任务等待前一个任务结束，然后再执行，程序的执行顺序与任务的排列顺序是一致的、同步的。</p>
<h2 id="异步"><a href="#异步" class="headerlink" title="异步"></a>异步</h2><p>“异步模式”则完全不同，每一个任务有一个或多个回调函数（callback），前一个任务结束后，不是执行后一个任务，而是执行回调函数，后一个任务则是不等前一个任务结束就执行，所以程序的执行顺序与任务的排列顺序是不一致的、异步的。</p>
<p>“异步模式” 非常重要。在浏览器端，耗时很长的操作都应该异步执行，避免浏览器失去响应，最好的例子就是 <code>Ajax</code> 操作。在服务器端，”异步模式”甚至是唯一的模式，因为执行环境是单线程的，如果允许同步执行所有 <code>http</code> 请求，服务器性能会急剧下降，很快就会失去响应。</p>
<p>本文总结了”异步模式”编程的 4 种方法，理解它们可以让你写出结构更合理、性能更出色、维护更方便的 Javascript 程序。</p>
<h1 id="一、回调函数"><a href="#一、回调函数" class="headerlink" title="一、回调函数"></a>一、回调函数</h1><p>这是异步编程最基本的方法。</p>
<p>假定有两个函数 <code>f1</code> 和 <code>f2</code>，后者等待前者的执行结果。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">　　f1();</span><br><span class="line"></span><br><span class="line">　　f2();</span><br></pre></td></tr></table></figure>

<p>如果 <code>f1</code> 是一个很耗时的任务，可以考虑改写 <code>f1</code>，把 <code>f2</code> 写成 <code>f1</code> 的回调函数。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">　　<span class="function"><span class="keyword">function</span> <span class="title">f1</span>(<span class="params">callback</span>)</span>&#123;</span><br><span class="line"></span><br><span class="line">　　　　setTimeout(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line"></span><br><span class="line">　　　　　　<span class="comment">// f1的任务代码</span></span><br><span class="line"></span><br><span class="line">　　　　　　callback();</span><br><span class="line"></span><br><span class="line">　　　　&#125;, <span class="number">1000</span>);</span><br><span class="line"></span><br><span class="line">　　&#125;</span><br></pre></td></tr></table></figure>

<p>执行代码就变成下面这样：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">　　f1(f2);</span><br></pre></td></tr></table></figure>

<p>采用这种方式，我们把同步操作变成了异步操作，<code>f1</code> 不会堵塞程序运行，相当于先执行程序的主要逻辑，将耗时的操作推迟执行。</p>
<p>回调函数的优点是简单、容易理解和部署，缺点是不利于代码的阅读和维护，各个部分之间高度耦合（Coupling），流程会很混乱，而且每个任务只能指定一个回调函数。</p>
<h1 id="二、事件监听"><a href="#二、事件监听" class="headerlink" title="二、事件监听"></a>二、事件监听</h1><p>另一种思路是采用事件驱动模式。任务的执行不取决于代码的顺序，而取决于某个事件是否发生。</p>
<p>还是以 <code>f1</code> 和 <code>f2</code> 为例。首先，为f1绑定一个事件（这里采用的 <code>jQuery</code> 的写法）。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">　　f1.on(<span class="string">'done'</span>, f2);</span><br></pre></td></tr></table></figure>

<p>上面这行代码的意思是，当 <code>f1</code> 发生 <code>done</code> 事件，就执行 <code>f2</code>。然后，对 <code>f1</code> 进行改写：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">　　<span class="function"><span class="keyword">function</span> <span class="title">f1</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line"></span><br><span class="line">　　　　setTimeout(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line"></span><br><span class="line">　　　　　　<span class="comment">// f1的任务代码</span></span><br><span class="line"></span><br><span class="line">　　　　　　f1.trigger(<span class="string">'done'</span>);</span><br><span class="line"></span><br><span class="line">　　　　&#125;, <span class="number">1000</span>);</span><br><span class="line"></span><br><span class="line">　　&#125;</span><br></pre></td></tr></table></figure>

<p><code>f1.trigger(&#39;done&#39;)</code> 表示，执行完成后，立即触发 <code>done</code> 事件，从而开始执行 <code>f2</code>。</p>
<p>这种方法的优点是比较容易理解，可以绑定多个事件，每个事件可以指定多个回调函数，而且可以”去耦合”（Decoupling），有利于实现模块化。缺点是整个程序都要变成事件驱动型，运行流程会变得很不清晰。</p>
<h1 id="三、发布-订阅"><a href="#三、发布-订阅" class="headerlink" title="三、发布/订阅"></a>三、发布/订阅</h1><p>上一节的”事件”，完全可以理解成”信号”。</p>
<p>我们假定，存在一个”信号中心”，某个任务执行完成，就向信号中心”发布”（publish）一个信号，其他任务可以向信号中心”订阅”（subscribe）这个信号，从而知道什么时候自己可以开始执行。这就叫做”发布/订阅模式”（publish-subscribe pattern），又称”观察者模式”（observer pattern）。</p>
<p>这个模式有多种实现，下面采用的是 Ben Alman的Tiny Pub/Sub，这是 <code>jQuery</code> 的一个插件。</p>
<p>首先，<code>f2</code> 向 “信号中心” <code>jQuery</code> 订阅 <code>&quot;done&quot;</code> 信号。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">　　jQuery.subscribe(<span class="string">"done"</span>, f2);</span><br></pre></td></tr></table></figure>

<p>然后，<code>f1</code> 进行如下改写：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">　　<span class="function"><span class="keyword">function</span> <span class="title">f1</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line"></span><br><span class="line">　　　　setTimeout(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line"></span><br><span class="line">　　　　　　<span class="comment">// f1的任务代码</span></span><br><span class="line"></span><br><span class="line">　　　　　　jQuery.publish(<span class="string">"done"</span>);</span><br><span class="line"></span><br><span class="line">　　　　&#125;, <span class="number">1000</span>);</span><br><span class="line"></span><br><span class="line">　　&#125;</span><br></pre></td></tr></table></figure>

<p><code>jQuery.publish(&quot;done&quot;)</code> 的意思是，<code>f1</code> 执行完成后，向 “信号中心” <code>jQuery</code> 发布 <code>&quot;done&quot;</code> 信号，从而引发 <code>f2</code> 的执行。</p>
<p>此外，<code>f2</code> 完成执行后，也可以取消订阅（unsubscribe）。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">　　jQuery.unsubscribe(<span class="string">"done"</span>, f2);</span><br></pre></td></tr></table></figure>

<p>这种方法的性质与”事件监听”类似，但是明显优于后者。因为我们可以通过查看”消息中心”，了解存在多少信号、每个信号有多少订阅者，从而监控程序的运行。</p>
<h1 id="四、Promises对象"><a href="#四、Promises对象" class="headerlink" title="四、Promises对象"></a>四、Promises对象</h1><p><code>Promises</code> 对象是 <code>CommonJS</code> 工作组提出的一种规范，目的是为异步编程提供统一接口。</p>
<p>简单说，它的思想是，每一个异步任务返回一个 <code>Promise</code> 对象，该对象有一个 <code>then</code> 方法，允许指定回调函数。比如，<code>f1</code> 的回调函数 <code>f2</code>,可以写成：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">　　f1().then(f2);</span><br></pre></td></tr></table></figure>

<p><code>f1</code> 要进行如下改写（这里使用的是 <code>jQuery</code> 的实现）：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">　　<span class="function"><span class="keyword">function</span> <span class="title">f1</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line"></span><br><span class="line">　　　　<span class="keyword">var</span> dfd = $.Deferred();</span><br><span class="line"></span><br><span class="line">　　　　setTimeout(<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line"></span><br><span class="line">　　　　　　<span class="comment">// f1的任务代码</span></span><br><span class="line"></span><br><span class="line">　　　　　　dfd.resolve();</span><br><span class="line"></span><br><span class="line">　　　　&#125;, <span class="number">500</span>);</span><br><span class="line"></span><br><span class="line">　　　　<span class="keyword">return</span> dfd.promise;</span><br><span class="line"></span><br><span class="line">　　&#125;</span><br></pre></td></tr></table></figure>

<p>这样写的优点在于，回调函数变成了链式写法，程序的流程可以看得很清楚，而且有一整套的配套方法，可以实现许多强大的功能。</p>
<p>比如，指定多个回调函数：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">　　f1().then(f2).then(f3);</span><br></pre></td></tr></table></figure>

<p>再比如，指定发生错误时的回调函数：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">　　f1().then(f2).fail(f3);</span><br></pre></td></tr></table></figure>

<p>而且，它还有一个前面三种方法都没有的好处：如果一个任务已经完成，再添加回调函数，该回调函数会立即执行。所以，你不用担心是否错过了某个事件或信号。这种方法的缺点就是编写和理解，都相对比较难。</p>

      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://yoursite.com/2016/07/21/%E5%8E%9F%E5%9E%8B%EF%BC%8C%E5%8E%9F%E5%9E%8B%E9%93%BE/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="https://inews.gtimg.com/newsapp_ls/0/12122532695/0">
      <meta itemprop="name" content="Albert Guo">
      <meta itemprop="description" content="郭鹏松的个人博客">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Albert Guo">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/2016/07/21/%E5%8E%9F%E5%9E%8B%EF%BC%8C%E5%8E%9F%E5%9E%8B%E9%93%BE/" class="post-title-link" itemprop="url">原型，原型链</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2016-07-21 13:50:51" itemprop="dateCreated datePublished" datetime="2016-07-21T13:50:51+08:00">2016-07-21</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2020-07-21 14:01:50" itemprop="dateModified" datetime="2020-07-21T14:01:50+08:00">2020-07-21</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/JavaScript/" itemprop="url" rel="index"><span itemprop="name">JavaScript</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <blockquote>
<p>本文部分摘录自网道 JavaScript 教程，仅供学习参考<br>参考地址：<a href="https://wangdoc.com/javascript/oop/prototype.html#prototype-%E5%B1%9E%E6%80%A7%E7%9A%84%E4%BD%9C%E7%94%A8" target="_blank" rel="noopener">https://wangdoc.com/javascript/oop/prototype.html#prototype-%E5%B1%9E%E6%80%A7%E7%9A%84%E4%BD%9C%E7%94%A8</a></p>
</blockquote>
<h1 id="prototype-属性的作用"><a href="#prototype-属性的作用" class="headerlink" title="prototype 属性的作用"></a>prototype 属性的作用</h1><p>JavaScript 继承机制的设计思想就是，原型对象的所有属性和方法，都能被实例对象共享。也就是说，如果属性和方法定义在原型上，那么所有实例对象就能共享，不仅节省了内存，还体现了实例对象之间的联系。</p>
<p>下面，先看怎么为对象指定原型。JavaScript 规定，每个函数都有一个 <code>prototype</code> 属性，指向一个对象。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">function f() &#123;&#125;</span><br><span class="line">typeof f.prototype &#x2F;&#x2F; &quot;object&quot;</span><br></pre></td></tr></table></figure>

<p>上面代码中，函数f默认具有 <code>prototype</code> 属性，指向一个对象。</p>
<p>对于普通函数来说，该属性基本无用。但是，对于构造函数来说，生成实例的时候，该属性会自动成为实例对象的原型。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">function Animal(name) &#123;</span><br><span class="line">  this.name &#x3D; name;</span><br><span class="line">&#125;</span><br><span class="line">Animal.prototype.color &#x3D; &#39;white&#39;;</span><br><span class="line"></span><br><span class="line">var cat1 &#x3D; new Animal(&#39;大毛&#39;);</span><br><span class="line">var cat2 &#x3D; new Animal(&#39;二毛&#39;);</span><br><span class="line"></span><br><span class="line">cat1.color &#x2F;&#x2F; &#39;white&#39;</span><br><span class="line">cat2.color &#x2F;&#x2F; &#39;white&#39;</span><br></pre></td></tr></table></figure>

<p>上面代码中，构造函数 <code>Animal</code> 的 <code>prototype</code> 属性，就是实例对象 <code>cat1</code> 和 <code>cat2</code> 的原型对象。原型对象上添加一个 <code>color</code> 属性，结果，实例对象都共享了该属性。</p>
<p>原型对象的属性不是实例对象自身的属性。只要修改原型对象，变动就立刻会体现在所有实例对象上。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Animal.prototype.color &#x3D; &#39;yellow&#39;;</span><br><span class="line"></span><br><span class="line">cat1.color &#x2F;&#x2F; &quot;yellow&quot;</span><br><span class="line">cat2.color &#x2F;&#x2F; &quot;yellow&quot;</span><br></pre></td></tr></table></figure>

<p>上面代码中，原型对象的 <code>color</code> 属性的值变为 <code>yellow</code> ，两个实例对象的 <code>color</code> 属性立刻跟着变了。这是因为实例对象其实没有 <code>color</code> 属性，都是读取原型对象的 <code>color</code> 属性。也就是说，==当实例对象本身没有某个属性或方法的时候，它会到原型对象去寻找该属性或方法。== 这就是原型对象的特殊之处。</p>
<p>==如果实例对象自身就有某个属性或方法，它就不会再去原型对象寻找这个属性或方法。==</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">cat1.color &#x3D; &#39;black&#39;;</span><br><span class="line"></span><br><span class="line">cat1.color &#x2F;&#x2F; &#39;black&#39;</span><br><span class="line">cat2.color &#x2F;&#x2F; &#39;yellow&#39;</span><br><span class="line">Animal.prototype.color &#x2F;&#x2F; &#39;yellow&#39;;</span><br></pre></td></tr></table></figure>

<p>上面代码中，实例对象 <code>cat1</code> 的 <code>color</code> 属性改为 <code>black</code>，就使得它不再去原型对象读取 <code>color</code> 属性，后者的值依然为 <code>yellow</code> 。</p>
<p>总结一下，==原型对象的作用，就是定义所有实例对象共享的属性和方法。== 这也是它被称为原型对象的原因，而实例对象可以视作从原型对象衍生出来的子对象。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Animal.prototype.walk &#x3D; function () &#123;</span><br><span class="line">  console.log(this.name + &#39; is walking&#39;);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>上面代码中，<code>Animal.prototype</code> 对象上面定义了一个walk方法，这个方法将可以在所有 <code>Animal</code> 实例对象上面调用。</p>
<p><img src="https://inews.gtimg.com/newsapp_ls/0/12123167721/0" alt="原型示意图"></p>
<h1 id="原型链"><a href="#原型链" class="headerlink" title="原型链"></a>原型链</h1><p>==JavaScript 规定，所有对象都有自己的原型对象（<code>prototype</code>）。一方面，任何一个对象，都可以充当其他对象的原型；另一方面，由于原型对象也是对象，所以它也有自己的原型。因此，就会形成一个“原型链”：对象到原型，再到原型的原型……==</p>
<p>如果一层层地上溯，所有对象的原型最终都可以上溯到 <code>Object.prototype</code>，即 <code>Object</code> 构造函数的 <code>prototype</code> 属性。也就是说，所有对象都继承了 <code>Object.prototype</code> 的属性。这就是所有对象都有 <code>valueOf</code> 和 <code>toString</code> 方法的原因，因为这是从 <code>Object.prototype</code> 继承的。</p>
<p><code>Object.prototype</code> 的原型是 <code>null</code> 。<code>null</code> 没有任何属性和方法，也没有自己的原型。因此，==原型链的尽头就是 <code>null</code>。==</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Object.getPrototypeOf(Object.prototype)</span><br><span class="line">&#x2F;&#x2F; null</span><br></pre></td></tr></table></figure>

<p>上面代码表示，<code>Object.prototype</code> 对象的原型是 <code>null</code>，由于 <code>null</code> 没有任何属性，所以原型链到此为止。<code>Object.getPrototypeOf</code> 方法返回参数对象的原型。</p>
<p>==读取对象的某个属性时，JavaScript  引擎先寻找对象本身的属性，如果找不到，就到它的原型去找，如果还是找不到，就到原型的原型去找。如果直到最顶层的 <code>Object.prototype</code> 还是找不到，则返回 <code>undefined</code> 。如果对象自身和它的原型，都定义了一个同名属性，那么优先读取对象自身的属性，这叫做“覆盖”。==</p>
<p>注意，一级级向上，在整个原型链上寻找某个属性，对性能是有影响的。所寻找的属性在越上层的原型对象，对性能的影响越大。如果寻找某个不存在的属性，将会遍历整个原型链。</p>
<p>举例来说，如果让构造函数的 <code>prototype</code> 属性指向一个数组，就意味着实例对象可以调用数组方法。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">var MyArray &#x3D; function () &#123;&#125;;</span><br><span class="line"></span><br><span class="line">MyArray.prototype &#x3D; new Array();</span><br><span class="line">MyArray.prototype.constructor &#x3D; MyArray;</span><br><span class="line"></span><br><span class="line">var mine &#x3D; new MyArray();</span><br><span class="line">mine.push(1, 2, 3);</span><br><span class="line">mine.length &#x2F;&#x2F; 3</span><br><span class="line">mine instanceof Array &#x2F;&#x2F; true</span><br></pre></td></tr></table></figure>

<p>上面代码中，<code>mine</code> 是构造函数 <code>MyArray</code> 的实例对象，由于 <code>MyArray.prototype</code> 指向一个数组实例，使得mine可以调用数组方法（这些方法定义在数组实例的 <code>prototype</code> 对象上面）。最后那行 <code>instanceof</code> 表达式，用来比较一个对象是否为某个构造函数的实例，结果就是证明 <code>mine</code> 为 <code>Array</code> 的实例。</p>
<p><img src="https://inews.gtimg.com/newsapp_ls/0/12123168822/0" alt="原型链示意图"></p>
<h1 id="constructor-属性"><a href="#constructor-属性" class="headerlink" title="constructor 属性"></a>constructor 属性</h1><p>==<code>prototype</code> 对象有一个 <code>constructor</code> 属性，默认指向 <code>prototype</code> 对象所在的构造函数。==</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">function P() &#123;&#125;</span><br><span class="line">P.prototype.constructor &#x3D;&#x3D;&#x3D; P &#x2F;&#x2F; true</span><br></pre></td></tr></table></figure>

<p>由于 <code>constructor</code> 属性定义在 <code>prototype</code> 对象上面，意味着可以被所有实例对象继承。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">function P() &#123;&#125;</span><br><span class="line">var p &#x3D; new P();</span><br><span class="line"></span><br><span class="line">p.constructor &#x3D;&#x3D;&#x3D; P &#x2F;&#x2F; true</span><br><span class="line">p.constructor &#x3D;&#x3D;&#x3D; P.prototype.constructor &#x2F;&#x2F; true</span><br><span class="line">p.hasOwnProperty(&#39;constructor&#39;) &#x2F;&#x2F; false</span><br></pre></td></tr></table></figure>

<p>上面代码中，<code>p</code> 是构造函数 <code>P</code> 的实例对象，但是 <code>p</code> 自身没有 <code>constructor</code> 属性，该属性其实是读取原型链上面的 <code>P.prototype.constructor</code> 属性。</p>
<p><code>constructor</code> 属性的作用是，可以得知某个实例对象，到底是哪一个构造函数产生的。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">function F() &#123;&#125;;</span><br><span class="line">var f &#x3D; new F();</span><br><span class="line"></span><br><span class="line">f.constructor &#x3D;&#x3D;&#x3D; F &#x2F;&#x2F; true</span><br><span class="line">f.constructor &#x3D;&#x3D;&#x3D; RegExp &#x2F;&#x2F; false</span><br></pre></td></tr></table></figure>

<p>上面代码中，<code>constructor</code> 属性确定了实例对象f的构造函数是 <code>F</code>，而不是 <code>RegExp</code>。</p>
<p>另一方面，有了 <code>constructor</code> 属性，就可以从一个实例对象新建另一个实例。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">function Constr() &#123;&#125;</span><br><span class="line">var x &#x3D; new Constr();</span><br><span class="line"></span><br><span class="line">var y &#x3D; new x.constructor();</span><br><span class="line">y instanceof Constr &#x2F;&#x2F; true</span><br></pre></td></tr></table></figure>

<p>上面代码中，<code>x</code> 是构造函数 <code>Constr</code> 的实例，可以从 <code>x.constructor</code> 间接调用构造函数。这使得在实例方法中，调用自身的构造函数成为可能。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Constr.prototype.createCopy &#x3D; function () &#123;</span><br><span class="line">  return new this.constructor();</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>上面代码中，<code>createCopy</code> 方法调用构造函数，新建另一个实例。</p>
<p><code>constructor</code> 属性表示原型对象与构造函数之间的关联关系，如果修改了原型对象，一般会同时修改 <code>constructor</code> 属性，防止引用的时候出错。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">function Person(name) &#123;</span><br><span class="line">  this.name &#x3D; name;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Person.prototype.constructor &#x3D;&#x3D;&#x3D; Person &#x2F;&#x2F; true</span><br><span class="line"></span><br><span class="line">Person.prototype &#x3D; &#123;</span><br><span class="line">  method: function () &#123;&#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">Person.prototype.constructor &#x3D;&#x3D;&#x3D; Person &#x2F;&#x2F; false</span><br><span class="line">Person.prototype.constructor &#x3D;&#x3D;&#x3D; Object &#x2F;&#x2F; true</span><br></pre></td></tr></table></figure>

<p>上面代码中，构造函数 <code>Person</code> 的原型对象改掉了，但是没有修改 <code>constructor</code> 属性，导致这个属性不再指向 <code>Person</code>。由于 <code>Person</code> 的新原型是一个普通对象，而普通对象的 <code>constructor</code> 属性指向 <code>Object</code> 构造函数，导致 <code>Person.prototype.constructor</code> 变成了 <code>Object</code>。</p>
<p>所以，修改原型对象时，一般要同时修改 <code>constructor</code> 属性的指向。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;&#x2F; 坏的写法</span><br><span class="line">C.prototype &#x3D; &#123;</span><br><span class="line">  method1: function (...) &#123; ... &#125;,</span><br><span class="line">  &#x2F;&#x2F; ...</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F; 好的写法</span><br><span class="line">C.prototype &#x3D; &#123;</span><br><span class="line">  constructor: C,</span><br><span class="line">  method1: function (...) &#123; ... &#125;,</span><br><span class="line">  &#x2F;&#x2F; ...</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">&#x2F;&#x2F; 更好的写法</span><br><span class="line">C.prototype.method1 &#x3D; function (...) &#123; ... &#125;;</span><br></pre></td></tr></table></figure>

<p>上面代码中，要么将 <code>constructor</code> 属性重新指向原来的构造函数，要么只在原型对象上添加方法，这样可以保证 <code>instanceof</code> 运算符不会失真。</p>
<p>如果不能确定 <code>constructor</code> 属性是什么函数，还有一个办法：通过 <code>name</code> 属性，从实例得到构造函数的名称。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">function Foo() &#123;&#125;</span><br><span class="line">var f &#x3D; new Foo();</span><br><span class="line">f.constructor.name &#x2F;&#x2F; &quot;Foo&quot;</span><br></pre></td></tr></table></figure>




      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  


  
  <nav class="pagination">
    <a class="extend prev" rel="prev" href="/"><i class="fa fa-angle-left" aria-label="上一页"></i></a><a class="page-number" href="/">1</a><span class="page-number current">2</span>
  </nav>



          </div>
          

<script>
  window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      let commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>

        </div>
          
  
  <div class="toggle sidebar-toggle">
    <span class="toggle-line toggle-line-first"></span>
    <span class="toggle-line toggle-line-middle"></span>
    <span class="toggle-line toggle-line-last"></span>
  </div>

  <aside class="sidebar">
    <div class="sidebar-inner">

      <ul class="sidebar-nav motion-element">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <!--noindex-->
      <div class="post-toc-wrap sidebar-panel">
      </div>
      <!--/noindex-->

      <div class="site-overview-wrap sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image" alt="Albert Guo"
      src="https://inews.gtimg.com/newsapp_ls/0/12122532695/0">
  <p class="site-author-name" itemprop="name">Albert Guo</p>
  <div class="site-description" itemprop="description">郭鹏松的个人博客</div>
</div>
<div class="site-state-wrap motion-element">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/archives">
          <span class="site-state-item-count">19</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
            <a href="/categories/">
          
        <span class="site-state-item-count">6</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
  </nav>
</div>
  <div class="links-of-author motion-element">
      <span class="links-of-author-item">
        <a href="mailto:albert-guo@outlook.com" title="E-Mail → mailto:albert-guo@outlook.com" rel="noopener" target="_blank"><i class="fa fa-envelope fa-fw"></i>E-Mail</a>
      </span>
  </div>



      </div>

    </div>
  </aside>
  <div id="sidebar-dimmer"></div>


      </div>
    </main>

    <footer class="footer">
      <div class="footer-inner">
        

        

<div class="copyright">
  
  &copy; 
  <span itemprop="copyrightYear">2020</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">Albert Guo</span>
</div>
  <div class="powered-by">由 <a href="https://hexo.io/" class="theme-link" rel="noopener" target="_blank">Hexo</a> & <a href="https://theme-next.org/" class="theme-link" rel="noopener" target="_blank">NexT.Gemini</a> 强力驱动
  </div>

        








      </div>
    </footer>
  </div>

  
  <script src="/lib/anime.min.js"></script>
  <script src="/lib/velocity/velocity.min.js"></script>
  <script src="/lib/velocity/velocity.ui.min.js"></script>

<script src="/js/utils.js"></script>

<script src="/js/motion.js"></script>


<script src="/js/schemes/pisces.js"></script>


<script src="/js/next-boot.js"></script>




  















  

  

</body>
</html>
